1 Introduction

Regularized regression modeling is a sophisticated approach that extends traditional regression techniques by incorporating a penalty term into the loss function. This penalty term constrains the magnitude of the regression coefficients, thereby controlling the complexity of the model and mitigating the risk of overfitting. The most common forms of regularized regression include Ridge Regression, which applies an \(L_2\) penalty to the sum of squared coefficients; LASSO, which uses an \(L_1\) penalty to encourage sparsity by shrinking some coefficients to zero; and Elastic Net, which combines both \(L_1\) and \(L_2\) penalties to balance their respective advantages. These methods are particularly useful in scenarios where traditional regression models struggle, such as when dealing with high-dimensional data or multicollinearity among predictors.

The primary objectives of regularized regression are multifaceted. First and foremost, it aims to improve the generalization ability of regression models by reducing overfitting. This is achieved by penalizing large coefficients, which helps the model focus on the most important patterns in the data. Second, regularized regression seeks to handle multicollinearity by stabilizing coefficient estimates, making them more reliable even when predictors are correlated. Third, it facilitates feature selection, particularly with LASSO, by shrinking less important coefficients to zero, thereby simplifying the model and improving interpretability. Finally, regularized regression strikes a balance between bias and variance, introducing a controlled amount of bias to reduce variance and enhance the robustness of the model.

This note is dedicated to regularized regression modeling methods, which have been recently developed by statisticians and are now widely adopted in the machine learning community. The mathematical content will be kept to a minimum to facilitate a high-level technical understanding of these methods.

2 \(L_p\) Norm

One of the key mathematical terms used in regularized regression methods is the \(L_p\) norm which defined as a distance between to points, \(\mathbf{x}\) and \(\mathbf{y}\) in a \(d-\)dimensional vector space with the following expression

\[ L_p(\mathbf{x} - \mathbf{y}) = \left[ \sum_{j=1}^d (x_j - y_j)^p\right]^{1/p} \stackrel{\text{defined}}{\equiv} ||\mathbf{x} - \mathbf{y}||_p. \] In the two dimensional vector space, \(\mathbf{x} = (x_1, x_2)\) and \(\mathbf{y} = (y_1, y_2)\), the the \(L_2\) norm is simply the following Euclidean distance

\[ ||\mathbf{x} - \mathbf{y}||_2 = \left[ (x_1-y_1)^2 + (x_2-y_2)^2\right]^{1/2} = \sqrt{(x_1-y_1)^2 + (x_2-y_2)^2} \]

Similarly, \(L_1\) norm of the above two points are given by

\[ ||\mathbf{x} - \mathbf{y}||_1 = \left[ (x_1-y_1)^1 + (x_2-y_2)^1\right]^{1/1} = |x_1-y_1| + |x_2-y_2| \] The corresponding \(L_{1/2}\) norm is given by

\[ ||\mathbf{x} - \mathbf{y}||_{1/2} = \left[ (x_1-y_1)^{1/2} + (x_2-y_2)^{1/2}\right]^{2} = \left( \sqrt{x_1-y_1} + \sqrt{x_2-y_2}\right)^2, \]

The following figure shows the geometry of various \(L_p\) norms including the above the

The geometry of $L_p$ norm

The geometry of \(L_p\) norm


3 General Framework of Regularization

Recall that the equations for linear and logistic regression models with \(k\) feature variables are given by, respectively

\[ y = \alpha_0 + \alpha_1 x_1 + \alpha_2 x_2 + \cdots + \alpha_k x_k \] and

\[ P(Y=1) = \frac{\exp(\beta_0 + \beta_1 x_1 + \cdots + \beta_k x_k)}{1 + \exp(\beta_0 + \beta_1 x_1 + \cdots + \beta_k x_k)} \stackrel{\text{defined}}{\equiv} p \]

The loss function (i.e., likelihood function) used to estimate the regression coefficients of linear and logistic regression models are given by respectively

\[ \text{LOSS}_{\text{linear}} = \sum_{i=1}^n (y_i - \hat{y}_i)^2 \]

and

\[ \text{LOSS}_{\text{logistic}} = -\frac{1}{n}\sum_{i=1}^n \left[ y_i\log(\hat{p}_i) + (1-y_i)\log(1-\hat{p}_i) \right]. \]

The working loss functions of commonly used regularized regression models are given by

  • L1 Regularization (Lasso Regression): Adds the absolute value of the coefficients as a penalty term to the loss function that shrinks some coefficients to zero, effectively performing feature selection. The actual loss function of the corresponding \(L_1\)-regularized linear and logistic regression models are given by respectively

\[ \text{LASSO}_{\text{linear}} = \text{LOSS}_{\text{linear}} + \lambda\sum_{j=1}^k |\alpha_j| \ \ \ \text{and} \ \ \ \text{LASSO}_{\text{logistic}} = \text{LOSS}_{\text{logistic}} + \gamma\sum_{j=1}^k |\beta_j| \]

The following geometry of the LASSO explains why it reduces dimension of the feature space (i.e., it drops feature variables mathematically)

  • L2 Regularization (Ridge Regression): Adds the squared magnitude of the coefficients as a penalty term to the loss function that Shrinks all coefficients but does not set them to zero, reducing their impact. The loss functions of the regularized linear and logistic models are given by

\[ \text{LASSO}_{\text{linear}} = \text{LOSS}_{\text{linear}} + \lambda\sum_{j=1}^k |\alpha_j|^2 \ \ \ \text{and} \ \ \ \text{LASSO}_{\text{logistic}} = \text{LOSS}_{\text{logistic}} + \gamma\sum_{j=1}^k |\beta_j|^2 \]

  • Lp Regularization (\(1< p <2\)) Elastic Net: Combines L1 and L2 regularization. The loss functions of the regularized linear and logistic models are given by respectively

\[ \text{LASSO}_{\text{linear}} = \text{LOSS}_{\text{linear}}+ \lambda_1\sum_{j=1}^k |\alpha_j| + \lambda_2\sum_{j=1}^k |\alpha_j|^2 \]

and

\[ \text{LASSO}_{\text{logistic}} = \text{LOSS}_{\text{logistic}} + \gamma_1\sum_{j=1}^k |\beta_j| + \gamma_2\sum_{j=1}^k |\beta_j|^2 \]

The geometry of the above regularized regression is depicted in a 2-dimensional feature space.

The geometry of regularized regression in 2-dimensional feature space.

The geometry of regularized regression in 2-dimensional feature space.

The above contour plots (shown in blue) represent the surface of the original loss functions, which does have the regularization penalty term. The coordinates of the center points correspond to the estimates of the regression coefficients. The coordinates of the intersection between the contours of the original loss function and the regularized loss function represent the estimated coefficients of the underlying regularized regression model.

Important Remark Regularization Techniques such as LASSO, Ridge, and Elasticnet are only different methods for estimating the regression coefficients (some of the regression coefficients original model may be dropped after performed some regularization techniques). In the next section, we use show the resulting regularized models with estimated regression coefficients


4 Regularization with R glmnet

The R library glmnet is primarily designed for implementing regularization techniques. It is often used in conjunction with the caret library to build and evaluate various regularized regression models. From a mathematical perspective, the optimization problem associated with regularized loss functions, which yield estimates of regression coefficients, can be viewed as a constrained maximization problem involving a Lagrange multiplier, denoted as \(\lambda\). This Lagrange multiplier \(\lambda\) acts as a hyperparameter and must be carefully tuned, typically through cross-validation, to achieve optimal model performance.

glmnet introduces a mixing parameter \(\alpha\) to formulate a unified framework in the following form

\[ \text{LOSS}_{\text{regularization}} = \text{LOSS}_{\text{original}} + \lambda \left[ \frac{(1-\alpha)}{2}||\beta||_2^2 + \alpha ||\beta||_1.\right] \]

That is, \(\alpha = 1\) yields the LASSO regression and \(\alpha = 0\) yields the ridge regression. All other values of \(0 \le \alpha \le 1\) represents the elastic net regularized regression.

5 Regularized Linear Regression

This section utilizes the widely-used Boston Housing data set (available in the R library MASS) to demonstrate how regularization linear regression can be applied to predict house prices in R.

This dataset contains information collected by the U.S Census Service concerning housing in the area of Boston Mass. It was obtained from the StatLib archive (http://lib.stat.cmu.edu/datasets/boston), and has been used extensively throughout the literature to benchmark algorithms. However, these comparisons were primarily done outside of Delve and are thus somewhat suspect. The dataset is small in size with only 506 cases. A copy of the data set can be found at: https://pengdsci.github.io/STA552/w07/BostonHousing.csv

  • CRIM - per capita crime rate by town
  • ZN - proportion of residential land zoned for lots over 25,000 sq.ft.
  • INDUS - proportion of non-retail business acres per town.
  • CHAS - Charles River dummy variable (1 if tract bounds river; 0 otherwise)
  • NOX - nitric oxides concentration (parts per 10 million)
  • RM - average number of rooms per dwelling
  • AGE - proportion of owner-occupied units built prior to 1940
  • DIS - weighted distances to five Boston employment centres
  • RAD - index of accessibility to radial highways
  • TAX - full-value property-tax rate per $10,000
  • PTRATIO - pupil-teacher ratio by town
  • B - 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
  • LSTAT - % lower status of the population
  • MEDV - Median value of owner-occupied homes in $1000’s

For simplicity, we leverage functions from two popular R libraries, glmnet and caret, to implement the regularization techniques.

5.1 Modeling Steps

The following are the basic steps in in the implementation

  • Setting seed to guarantee reproducibility
  • Random Data Splitting for training, cross-validation, and testing
  • Standardizing feature variables - this is extremely important in regularization!
  • building regularized linear regression models
  • Predicting the response with the testing data set
  • Evaluate the performance measure: root-mean-squared-error (RMSE)
  • finding the optimal Lagrange multipliers through cross-validation
  • reporting the final resulting regularized regression models (with estimated regression coefficients)

In next few subsections, we introduce the major steps to be considered in building regularized regression models.

5.1.1 Coefficient Path Analysis

\(\lambda\) is the regularization parameter in glmnet() that controls the strength of the penalty applied to the coefficients. A larger \(\lambda\) shrinks the coefficients more aggressively, potentially reducing overfitting but increasing bias. A smaller \(\lambda\) allows the model to fit the data more closely but may lead to overfitting.

We first examine how the hyperparameter \(\lambda\) penalizes the regression coefficients and affects model fit. Coefficient path analysis and the plot of the goodness-of-fit measure (RMSE) are commonly used visual tools for model selection.

set.seed(112233)    # remove the seed by using  set.seed(NULL)

# Load the dataset
data("Boston")
X <- as.matrix(Boston[, -14])  # Features (all columns except the target)
y <- Boston$medv  # Target variable (median house value)

# Split the data into training and testing sets
train_index <- createDataPartition(y, p = 0.8, list = FALSE)
X_train <- X[train_index, ]
X_test <- X[-train_index, ]
y_train <- y[train_index]
y_test <- y[-train_index]

# Standardize the data (important for regularization)
preprocess_params <- preProcess(X_train, method = c("center", "scale"))
X_train <- predict(preprocess_params, X_train)
X_test <- predict(preprocess_params, X_test)

## fitting the model
fit_lasso<- glmnet(X_train, 
                      y_train, 
                      alpha = 1)        # lasso regression 
fit_ridge <- glmnet(X_train, 
                    y_train, 
                    alpha = 0)          # Ridge regression
fit_elastic_net <- glmnet(X_train, 
                          y_train, 
                          alpha = 0.5)  # elastic net

## cross-validation
cv_lasso <- cv.glmnet(X_train, y_train, alpha = 1)   # lasso regression
cv_ridge <- cv.glmnet(X_train, y_train, alpha = 0)   # Ridge regression
cv_elastic_net <- cv.glmnet(X_train, y_train, alpha = 0.5)  # elastic net
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(fit_lasso, xvar = "lambda", label = TRUE,
     lwd = 1.5,
     main = "Coefficient Path Analysis: LASSO",
     cex.main = 0.9,
     col = rainbow(10))
abline(v = 1, col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)

The coefficient path plot illustrates how the value of \(\lambda\) affects the degree of shrinkage applied to individual coefficients. The numbers at the top of the plot indicate the number of feature variables retained in the model for a given choice of \(\lambda\). For instance, when \(\log(\lambda)=1\), three variables remain in the model. By tracing the paths, we can identify the IDs of these three feature variables (i.e., 6, 11, and 13). The names of these variables can be retrieved using the R command colnames(X_train)[c(6,11,13)], which yields rm, ptratio, and lstat.

Similarly, when \(log(\lambda)= -1\), nine feature variables are retained in the model, with variable IDs: 6, 12, 4, 3, 1, 5, 8, 11, and 13. The corresponding variable names can be found using the command colnames(X_train)[c(6,12,4,3,1,5,8,11,13)], which returns rm, black, chas, indus, crim, nox, dis, ptratio, and lstat.

Additionally, the plot demonstrates that as \(\lambda\) increases, the regression coefficients shrink, and some of them converge to zero. This indicates that certain feature variables are dropped from the model as \(\lambda\) becomes larger.

par(mar=c(5,4,6,3))
##
plot(cv_lasso, main = "RMSE Plot: LASSO",
     cex.main = 0.9)

The above performance plot shows that as \(\lambda\) increases, the MSE increases. The two vertical lines give the reference of the choice of \(\lambda\). For avoid overfitting and underfitting, \(\log(\lambda)\) should be between the two vertical lines.

The performance plot above shows that as \(\lambda\) increases, the MSE also increases. The two vertical lines indicate reference points for selecting \(\lambda\). To avoid overfitting and underfitting, \(\log(\lambda)\) should lie between these two lines.

5.2 Tuning Regularization Parameter

The two values of \(\lambda\) corresponding to the two vertical lines on the above plot of goodness-of-fit measure was calculated based on the cross validation. In glmnet(), 10-fold cross-validation was used to tune \(\lambda\) in cv.glmnet(). This process is repeated for each fold, and the average prediction error (e.g., mean squared error for regression or deviance for classification) is computed for each lambda. With the 10-cross-validation, cv.glmnet() returns

  • lambda: The sequence of lambda values tested.

  • cvm: The mean cross-validated error for each lambda.

  • cvsd: The standard error of the cross-validated error.

  • lambda.min: The value of lambda that gives the minimum cross-validated error.

  • lambda.1se: The largest value of lambda such that the error is within 1 standard error of the minimum. This is often used to select a more parsimonious model.

The two vertical lines on the above performance plot correspond to lambda.min and lambda.1se, respectively.

# Cross-validation to find the best lambda
cv_lasso <- cv.glmnet(X_train, y_train, alpha = 1)
cv_ridge <- cv.glmnet(X_train, y_train, alpha = 0)
cv_elastic_net <- cv.glmnet(X_train, y_train, alpha = 0.5)
##
# Extract coefficients for the best lambda
best.lasso.lambda <- cv_lasso$lambda.min
best.ridge.lambda <- cv_ridge$lambda.min
best.elastic.net.lambda <- cv_elastic_net$lambda.min
##
# Lasso Regression (L1 Regularization): 
# CAUTION: model formula differs from the regular regression formula 
lasso_model.opt <- glmnet(X_train, 
                      y_train, 
                      alpha = 1,      # lasso regression 
                      lambda = best.lasso.lambda)   # useser selected alpha, optimal lambda
                                      # can be obtained through CV (see below)
lasso_predictions.opt <- predict(lasso_model.opt, 
                             s = best.lasso.lambda, # user selected lambda value 
                                      # (regularization paremeter)
                             newx = X_test)  # test data set 
# The following RMSE of prediction serves as a validation - one step validation
lasso_rmse.opt <- sqrt(mean((y_test - lasso_predictions.opt)^2))   
                                          
# Ridge Regression (L2 Regularization)
ridge_model.opt <- glmnet(X_train, y_train, alpha = 0, lambda = best.ridge.lambda)
ridge_predictions.opt <- predict(ridge_model.opt, s = best.ridge.lambda, newx = X_test)
ridge_rmse.opt <- sqrt(mean((y_test - ridge_predictions.opt)^2))

# Elastic Net (Combination of L1 and L2)
elastic_net_model.opt <- glmnet(X_train, y_train, alpha = 0.5, lambda = 0.1)
elastic_net_predictions.opt <- predict(elastic_net_model.opt, s = 0.1, newx = X_test)
elastic_net_rmse.opt <- sqrt(mean((y_test - elastic_net_predictions.opt)^2))

RMSE.opt = cbind(LASSO.opt = lasso_rmse.opt, 
                 Ridge.opt =  ridge_rmse.opt, 
                 Elasticnet.opt = elastic_net_rmse.opt)
pander(RMSE.opt)
LASSO.opt Ridge.opt Elasticnet.opt
5.563 5.684 5.587

5.3 Extracting Final Model

Recall that the objective of regularized regression is to use the regularization techniques to estimate the regression coefficients. The outcome is the explicit regression equation for practical application. This subsection extracts the regression coefficients and write the regression function explicitly.

Note that the final model in regularized regression is dependent on the choice of the regularization parameter \(\lambda\). The following final models are based on the value of \(\lambda\) that gives the minimum cross-validated error, lambda.min trunded from cv.glmnet().

LASSO Regression Equation

The resulting LASSO regression equation is given by

##lasso
# Extract coefficients for the best lambda
best_lambda.lasso <- cv_lasso$lambda.min
coefficients.lasso <- coef(cv_lasso, s = best_lambda.lasso)
# Reconstruct the model equation
intercept.lasso <- coefficients.lasso[1]
betas.lasso <- coefficients.lasso[-1]
#cat("Model equation: y =", round(intercept.lasso,4), "+", 
#paste(round(betas.lasso,4), colnames(X), sep = "*", collapse = " + "), "\n")

\[ \text{ Price} = 22.3381 -0.9642\times \text{crim} + 1.0654\times \text{zn} -0.2763\times \text{indus} + 0.6528\times \text{chas} -1.7685\times \text{nox} \] \[ + 2.3679\times \text{rm} -0.0072\times \text{age} -2.9794\times \text{dis} + 2.3692\times \text{rad} -1.7409\times \text{tax} \] \[ -1.9246\times \text{ptratio} + 0.9447\times \text{black} -3.5858\times \text{lstat} \]

Ridge Regression Equation

The resulting Ridge regression equation is given by

##ridge
# Extract coefficients for the best lambda
best_lambda.ridge <- cv_ridge$lambda.min
coefficients.ridge <- coef(cv_ridge, s = best_lambda.ridge)
# Reconstruct the model equation
intercept.ridge <- coefficients.ridge[1]
betas.ridge <- coefficients.ridge[-1]
#cat("Model equation: y =", round(intercept.ridge,4), "+", 
#paste(round(betas.ridge,4), colnames(X), sep = "*", collapse = " + "), "\n")

\[ \text{price} = 22.3381 -0.8402\times \text{crim} + 0.8157\times \text{zn} -0.5426\times \text{indus} + 0.7197\times \text{chas} -1.247\times \text{nox} \] \[ + 2.5183\times \text{rm} -0.1387\times \text{age} -2.3727\times \text{dis} + 1.3862\times \text{rad} -0.9517\times \text{tax} \] \[ -1.7439\times \text{ptratio} + 0.9391\times \text{black} -3.2138\times \text{lstat} \]

ElasticNet Regression Equation

The resulting ElasticNet regression equation is given by

##ridge
# Extract coefficients for the best lambda
best_lambda.net <- cv_elastic_net$lambda.min
coefficients.net <- coef(cv_elastic_net, s = best_lambda.net)
# Reconstruct the model equation
intercept.net <- coefficients.net[1]
betas.net<- coefficients.net[-1]
#cat("Model equation: y =", round(intercept.net,4), "+", 
# paste(round(betas.net,4), colnames(X), sep = "*", collapse = " + "), "\n")

\[ \text{price} = 22.3381 -1.0027\times \text{crim} + 1.1069\times \text{zn} -0.2632\times \text{indus} + 0.6585\times \text{chas} -1.8166\times \text{nox} \] \[ + 2.3552\times \text{rm} -0.0369\times \text{age} -3.0683\times \text{dis} + 2.5507\times \text{rad} -1.8915\times \text{tax} \] \[ -1.9414\times \text{ptratio} + 0.9562\times \text{black} -3.5733\times \text{lstat} \]

5.4 Interaction and Polymoial

Since glmnet defines the model using two components: design matrix and response matrix (vector), this means that we have to define interaction terms and high order polynomials of feature variables directly in the design matrix. The following example demonstrates how to include interaction term in regularized regression model using a built-in data set mtcars.

data(mtcars)

# Prepare data
X <- as.matrix(mtcars[, c("hp", "wt")])  # Predictors
y <- mtcars$mpg  # Response

# Create interaction term
interaction_term <- X[, "hp"] * X[, "wt"]
X_with_interactions <- cbind(X, interaction_term)

# Fit glmnet model
fit <- glmnet(X_with_interactions, y, alpha = 1)  # this fits LASSO regression
# Extracts coefficients at lambda = 0.1
# 
#coef(fit, s = 0.1)    
# Cross-validation
cv_fit <- cv.glmnet(X_with_interactions, y, alpha = 1)

# Plot the cross-validation results for manually select the value of lambda
#plot(cv_fit)

# Best lambda value: automatically selected optimal lambda
best_lambda <- cv_fit$lambda.min
#print(best_lambda)

# Refit the model with the best lambda
final_model <- glmnet(X_with_interactions, y, alpha = 1, lambda = best_lambda)
coef(final_model, s = best_lambda)
4 x 1 sparse Matrix of class "dgCMatrix"
                          s1
(Intercept)      49.38307566
hp               -0.11718926
wt               -8.07164991
interaction_term  0.02693828


6 Regularized Logistic Regression

Implementing regularized logistic regression (LASSO, Ridge, and Elastic Net) using the glmnet package in R involves several steps.

  • Setting seed to guarantee reproducibility
  • Random Data Splitting for training, cross-validation, and testing
  • Standardizing feature variables - this is extremely important in regularization!
  • building regularized logistic regression models including cross-validation for \(\lambda\)
  • Predicting the response with the testing data set using the default cut-off probability
  • Evaluate the performance measure: confusion matrix
  • Comparing candiate models and visualizing results
  • Reporting the final resulting regularized regression models (with estimated regression coefficients)

Next, we us the popular Pima Indian Diabetes data set in R library mlbench to demonstrate the implementation of regularized logistic regression. The dataset is originally from the National Institute of Diabetes and Digestive and Kidney Diseases. The objective of the dataset is to diagnostically predict whether or not a patient has diabetes, based on certain diagnostic measurements included in the dataset. Several constraints were placed on the selection of these instances from a larger database. In particular, all patients here are females at least 21 years old of Pima Indian heritage.

The data sets consists of several medical predictor variables and one target variable, Outcome with 768 observations.

pregnant Number of times pregnant.

glucose Plasma glucose concentration (glucose tolerance test).

pressure Diastolic blood pressure (mm Hg).

triceps Triceps skin fold thickness (mm).

insulin 2-Hour serum insulin (mu U/ml).

mass Body mass index (weight in kg/(height in m)^2).

pedigree Diabetes pedigree function.

age Age (years).

diabetes Factor indicating the diabetes test result (neg/pos = 0/1).

The data set is also available at https://pengdsci.github.io/STA552/w07/diabetes.csv

The objective of performing logistic modeling is to predict diabetes based on variable risk factors. We will follow the above steps to perform three regularized logistic regression models.

6.1 Coefficient Path Analysis

In regularized regression, the coefficient path and measures of fit are essential tools for understanding model performance and selecting the optimal regularization parameter.

# loading related packages
# library(mlbench)
# library(glmnet)
# library(caret)
data("PimaIndiansDiabetes")
df <- PimaIndiansDiabetes

# Convert the response variable to a binary numeric variable
df$diabetes <- ifelse(df$diabetes == "pos", 1, 0)

# Split the data into predictors (X) and response (y)
X <- model.matrix(diabetes ~ ., df)[,-1]  # Remove the intercept column
y <- df$diabetes

# Split the data into training and testing sets
set.seed(123)
trainIndex <- createDataPartition(y, p = 0.8, list = FALSE)
X_train <- X[trainIndex, ]
X_test <- X[-trainIndex, ]
y_train <- y[trainIndex]
y_test <- y[-trainIndex]

####################
# Fit LASSO model
####################
lasso_model <- glmnet(X_train, y_train, family = "binomial", alpha = 1)

# Cross-validation to find the optimal lambda
cv_lasso <- cv.glmnet(X_train, y_train, family = "binomial", alpha = 1)

# Optimal lambda
lambda_lasso <- cv_lasso$lambda.min

# Refit the model with the optimal lambda
lasso_model_opt <- glmnet(X_train, y_train, 
                          family = "binomial", 
                          alpha = 1, 
                          lambda = lambda_lasso)
## Visualize the impact of lambda on shrinking coefficients
# Plot coefficient paths
par(mar=c(5,4,6,2), mfrow=c(1,2)) # 
plot(lasso_model, xvar = "lambda", label = TRUE,
     col = rainbow(8),
     lwd = 1,
     main = "Coefficient Path Plot: LASSO",
     cex.main = 0.8)
text(-6, 0.4, "minimum CV error", col="red", cex = 0.6 )
abline(v = log(cv_fit$lambda.min), col = "red", lty = 4, lwd = 1)
abline(v = log(cv_fit$lambda.1se), col = "blue", lty = 4, lwd = 1)

plot(cv_lasso, main="Measure of Model Fit: LASSO", cex.main = 0.8)

First, since \(\lambda\) in regularized regression models is typically a small positive number, a logarithmic scale was used for the coefficient path and performance fit plots. As \(\lambda\) increases, we observe that the magnitude of the coefficients decreases (left panel), while the deviance (error) increases (right panel). The two vertical reference lines serve as guides for selecting an appropriate value of \(\lambda\) in practical applications.

  • A larger value of \(\lambda\) can lead to potential underfitting, as more coefficients are shrunk toward zero (left panel), resulting in a larger error (right panel).

  • A smaller value of \(\lambda\) can lead to potential overfitting, as fewer coefficients are shrunk, allowing more to remain in the model, which results in a smaller error.

6.2 Regularization Parameter Determination

To avoid potential issues of overfitting and underfitting, a cross-validation was used in glmnet to provide a range of suggested value for the regularization parameter \(\lambda\): the minimum \(\lambda\) to avoid overfitting and biggest \(\lambda\) to avoid underfitting of the model.

####################
# Fit Ridge model
####################
ridge_model <- glmnet(X_train, y_train, family = "binomial", alpha = 0)

# Cross-validation to find the optimal lambda
cv_ridge <- cv.glmnet(X_train, y_train, family = "binomial", alpha = 0)

# Optimal lambda
lambda_ridge <- cv_ridge$lambda.min

# Refit the model with the optimal lambda
ridge_model_opt <- glmnet(X_train, y_train, 
                          family = "binomial", 
                          alpha = 0, 
                          lambda = lambda_ridge)
############################################
# Fit Elastic Net model (e.g., alpha = 0.5)
############################################
elastic_model <- glmnet(X_train, y_train, family = "binomial", alpha = 0.5)

# Cross-validation to find the optimal lambda
cv_elastic <- cv.glmnet(X_train, y_train, family = "binomial", alpha = 0.5)

# Optimal lambda
lambda_elastic <- cv_elastic$lambda.min

# Refit the model with the optimal lambda
elastic_model_opt <- glmnet(X_train, y_train, 
                            family = "binomial", 
                            alpha = 0.5, 
                            lambda = lambda_elastic)
lasso.coef <- as.matrix(coef(lasso_model_opt))
ridge.coef <- as.matrix(coef(ridge_model_opt))
elastic.coef <- as.matrix(coef(elastic_model_opt))
regularized.coef <- data.frame(lasso = lasso.coef[,1],
                               ridge = ridge.coef[,1],
                          elasticnet = elastic.coef[,1])
pander(regularized.coef)
  lasso ridge elasticnet
(Intercept) -7.844 -7.13 -7.518
pregnant 0.1101 0.09804 0.1036
glucose 0.03369 0.029 0.03183
pressure -0.01073 -0.009307 -0.009405
triceps 0 -0.0006965 0
insulin -0.0006115 -0.0003435 -0.000403
mass 0.07855 0.07118 0.0739
pedigree 0.6778 0.658 0.6326
age 0.01385 0.01607 0.01392

6.3 Optimal Cut0ff Probability Determination

Optimal Cut-off Probability Determination

In order use the fitted model for predicting the label or classification, we need to identify the optimal cut-off probability instead of using the default cut-off probability of 0.5.

#############################
# Predict on the test set: type = "class" uses the default 
# cut-off probability to be 0.5.
predict_lasso <- predict(lasso_model_opt, newx = X_test, type = "response")
predict_ridge <- predict(ridge_model_opt, newx = X_test, type = "response")
predict_elastic <- predict(elastic_model_opt, newx = X_test, type = "response")

###########################################
## Optimal cutoff probability determination
seq.cut <- seq(0,1, length=50)
# y is a vector of 0 and 1
acc.lasso <- NULL
acc.ridge <- NULL
acc.elastic <- NULL
for (i in 1:length(seq.cut)){
   predy.lasso <- ifelse(predict_lasso >seq.cut[i], 1, 0)
   predy.ridge<- ifelse(predict_ridge >seq.cut[i], 1, 0)
   predy.elastic<- ifelse(predict_elastic >seq.cut[i], 1, 0)
   ##
   acc.lasso[i] <- mean(y_test  == predy.lasso)
   acc.ridge[i] <- mean(y_test == predy.ridge)
   acc.elastic[i] <- mean(y_test  == predy.elastic)
}
## optimal cut-off: if the maximum accuracy occurs at multiple
## cut-off probabilities, the average of these cutoff probabilities
## will be defined as the optimal cutoff probability
opt.cut.lasso <- mean(seq.cut[which(acc.lasso==max(acc.lasso))])
opt.cut.ridge<- mean(seq.cut[which(acc.ridge==max(acc.ridge))])
opt.cut.elastic <- mean(seq.cut[which(acc.elastic==max(acc.elastic))])
##
acc.data <- data.frame(prob = rep(seq.cut,3), 
                       acc=c(acc.lasso, acc.ridge, acc.elastic), 
                       group = c(rep("lasso",50), rep("ridge",50), rep("elastic",50)))
##
gg.acc <- ggplot(data = acc.data, aes(x=prob, y = acc, color = group)) +
  geom_line() +
  annotate("text", x = 0.6, y = 0.45, 
           label = paste("LASSO cutoff: ", round(opt.cut.lasso,5), "Accuracy: ", round(max(acc.lasso),5), 
                         "\nRidge cutoff: ", round(opt.cut.ridge,5), "Accuracy: ", round(max(acc.ridge),5), 
                         "\nElastic cutoff: ", round(opt.cut.elastic,5), "Accuracy: ", round(max(acc.elastic),5)), 
           size = 3, 
           color = "navy") +
  ggtitle("Cut-off Probability vs Accuracy") +
  labs(x = "cut-off Probability", 
       y = "accuracy", color = "Group") +
  theme(plot.title = element_text(hjust = 0.5))

##
ggplotly(gg.acc)

The figure shows the optimal cutoff probabilities of the three regularized regression models.

Local Performance Measures

With the optimal cut-off probabilities of the corresponding three regularized logistic regression models, we now calculate the local performance measures based on their confusion matrices.

Ref. Positive Ref. Negative
Pred. Positive A B
Pred. Negative C D

Various local measures are summarized in the following

  • Sensitivity \(\text{sen} = A/(A + C)\)
  • Specificity \(\text{spe} = D/(B + D)\)
  • Prevalence \(\text{Prevalence} =(A + C)/(A + B + C + D)\)
  • Positive Predictive Value

\[ \text{PPV} = \frac{\text{sen}\times \text{prevalence}}{\text{sen}\times \text{prevalence} + (1-\text{sen})\times (1-\text{prevalence})} \]

  • Negative Predicted Value

\[ \text{PPV} = \frac{\text{spe}\times (1-\text{prevalence})}{(1-\text{spe})\times \text{prevalence} + \text{spe}\times (1-\text{prevalence})} \]

  • Detection Rate \(\text{DetectionRate = A/(A + B + C + D)}\)

  • Detection Prevalence \(\text{DetectionPrevalence}=(A + B)/ (A + B + C + D)\)

  • Balanced Accuracy \(\text{BalancedAccuracy} = (\text{sen} + \text{spe})/2\)

  • Precision \(\text{Precision} = A/(A + B)\)

  • Recall \(\text{Recall} = A/(A + C)\)

  • F1 Score \(F1 = (1+\beta^2)\times \text{Precision}\times \text{Recall}(\beta^2\times \text{Precision} + \text{Recall})\)

R function confusionMatrix() in library caret calculates the above local measures.

#######################################
## using the optimal cutoff probability to predict labels
## 
pred.lab.lasso <- ifelse(predict_lasso >opt.cut.lasso, 1, 0)
pred.lab.ridge<- ifelse(predict_ridge >opt.cut.ridge, 1, 0)
pred.lab.elastic<- ifelse(predict_elastic >opt.cut.elastic, 1, 0)


#################################
# Convert predictions to factors
pred.lab.lasso.fct <- as.factor(pred.lab.lasso)
pred.lab.ridge.fct <- as.factor(pred.lab.ridge)
pred.lab.elastic.fct <- as.factor(pred.lab.elastic)

# Convert actual values to factors
y_test <- as.factor(y_test)

# Confusion Matrix and Metrics
confusion.lasso <- confusionMatrix(pred.lab.lasso.fct, y_test)
confusion.ridge<- confusionMatrix(pred.lab.ridge.fct, y_test)
confusion.elastic <- confusionMatrix(pred.lab.elastic.fct, y_test)

## Commonly used performance measured
PerfMeasures <- cbind(lasso = confusion.lasso$byClass, 
                     ridge = confusion.ridge$byClass, 
                     elastic = confusion.elastic$byClass)
pander(PerfMeasures)
  lasso ridge elastic
Sensitivity 0.9406 0.9406 0.9208
Specificity 0.5192 0.5 0.5192
Pos Pred Value 0.7917 0.7851 0.7881
Neg Pred Value 0.8182 0.8125 0.7714
Precision 0.7917 0.7851 0.7881
Recall 0.9406 0.9406 0.9208
F1 0.8597 0.8559 0.8493
Prevalence 0.6601 0.6601 0.6601
Detection Rate 0.6209 0.6209 0.6078
Detection Prevalence 0.7843 0.7908 0.7712
Balanced Accuracy 0.7299 0.7203 0.72

6.4 ROC Analysis

To compare the performance of the three regularized logistic regression models (LASSO, Ridge, and Elastic Net) using ROC analysis, we can calculate the Area Under the Curve (AUC) and plot the ROC curves. The pROC package in R is particularly useful for this purpose.

# library(pROC)
# Predicted probabilities for each model: type = "response"
prob_lasso <- predict(lasso_model_opt, newx = X_test, type = "response")
prob_ridge <- predict(ridge_model_opt, newx = X_test, type = "response")
prob_elastic <- predict(elastic_model_opt, newx = X_test, type = "response")

# Compute ROC curves: roc object contains a lot information including
# sensitivity, specificity, AUC, etc.
roc_lasso <- roc(y_test, prob_lasso)
roc_ridge <- roc(y_test, prob_ridge)
roc_elastic <- roc(y_test, prob_elastic)

# Compute AUC values
auc_lasso <- auc(roc_lasso)
auc_ridge <- auc(roc_ridge)
auc_elastic <- auc(roc_elastic)

## LASSO
sen.lasso <- roc_lasso$sensitivities
spe.lasso <- roc_lasso$specificities
auc.lasso <- roc_lasso$auc

## Ridge
sen.ridge <- roc_ridge$sensitivities
spe.ridge <- roc_ridge$specificities
auc.ridge <- roc_ridge$auc

## Elastic Net
sen.elastic <- roc_elastic$sensitivities
spe.elastic <- roc_elastic$specificities
auc.elastic <- roc_elastic$auc

## Plotting the ROC curves: three colors - green, orange, and purple

plot(1-spe.lasso, sen.lasso, 
     type = "l",
     col = "green", 
     xlim=c(0,1),
     xlab = "1 - specificity",
     ylab = "sensitivity",
     main = "ROC Curves for LASSO, Ridge, and Elastic Net")
lines(1-spe.ridge, sen.ridge, col = "orange")
lines(1-spe.elastic, sen.elastic, col = "purple")
abline(0,1, type = "l", lty = 2, col = "steelblue", lwd = 1)

# Add legend
legend("bottomright", legend = c(paste("LASSO (AUC =", round(auc_lasso, 3), ")"),
                                paste("Ridge (AUC =", round(auc_ridge, 3), ")"),
                                paste("Elastic Net (AUC =", round(auc_elastic, 3), ")")),
       col = c("green", "orange", "purple"), lty = 1, cex = 0.8, bty = "n")

The above ROC curves and the corresponding AUCs are similar to each other. That is, the three regularized regression performed equally well.

7 Model Selection of Regularized Regression

If the candidate regularized regression models are equally well-performed, the following practical considerations are recommended when selecting the final model.

Implementation: If you are deploying the model in a production environment, consider the ease of implementation and maintenance. Lasso models are often easier to interpret and explain to stakeholders.

Scalability: For very large datasets, Lasso might be more scalable due to its sparsity.

Regularization Path: If you want to explore the entire regularization path (e.g., for visualization or analysis), all three methods (Lasso, Ridge, Elastic Net) support this, but Lasso and Elastic Net provide more interpretable paths due to feature selection.

The following table summarized some of the criteria to be considered in selecting the final working model from the equally well-performed regularized regression models.

Criterion Lasso Ridge Elastic Net
Feature Selection Yes (sparse models) No (retains all predictors) Yes (sparse, but less than Lasso)
Multicollinearity Handles moderately Handles well Handles well
Interpretability High (fewer predictors) Lower (all predictors retained) Moderate
Computational Cost Low Low Moderate
Use Case High-dimensional data, feature selection Multicollinearity, small datasets Balanced approach, grouped predictors

8 About Inference of Regularized Regression

Regularized regression is a technique used to prevent overfitting in statistical models, particularly in the context of linear regression. Overfitting occurs when a model learns the noise in the training data rather than the underlying pattern, leading to poor generalization on unseen data. Regularization introduces a penalty term to the loss function, which constrains the magnitude of the model’s coefficients, thereby controlling the model’s complexity.

About the Inference of Regularized Regression

Inference in regularized regression involves understanding the properties of the estimated coefficients and making statistical inferences about them. However, traditional methods of inference (e.g., p-values, confidence intervals) are not directly applicable due to the bias introduced by the regularization penalty.

  • Bias-Variance Tradeoff: Regularization introduces bias into the model to reduce variance, leading to better generalization. This tradeoff is crucial for model performance.

  • Choice of Regularization Parameter (\(\lambda\)): The regularization parameter \(\lambda\) controls the strength of the penalty. It is typically chosen via cross-validation to balance bias and variance.

  • Degrees of Freedom: The effective degrees of freedom in regularized models are reduced due to the constraints imposed by the penalty term. This affects the model’s complexity and the interpretation of results.

  • Post-Selection Inference: After selecting variables using Lasso, traditional inference methods can be biased. Techniques like selective inference or bootstrap methods are used to make valid inferences.

  • Bayesian Interpretation: Regularized regression can be viewed through a Bayesian lens, where the penalty term corresponds to a prior distribution on the coefficients. For example, Ridge regression corresponds to a Gaussian prior, and Lasso corresponds to a Laplace prior.

Some Practical Considerations

  • Standardization: It is essential to standardize the predictors before applying regularization, as the penalty term is sensitive to the scale of the coefficients.

  • Computational Efficiency: Efficient algorithms like coordinate descent are often used to solve the optimization problems in regularized regression.

  • Interpretability: Regularized models, especially Lasso, can enhance interpretability by selecting a subset of relevant features.

In summary, regularized regression is a powerful tool for improving model performance and interpretability, but it requires careful consideration of the tradeoffs and appropriate methods for inference.

LS0tDQp0aXRsZTogJ1JlZ3VsYXJpemF0aW9uIFRlY2huaXF1ZXMgaW4gTWFjaGluZSBMZWFybmluZycNCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiICINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBgez1odG1sfQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8NCg0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC1zaXplOiAyNHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KfQ0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikNCmxpYnJhcnkoR0dhbGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJnbG1uZXQiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2xtbmV0IikNCmxpYnJhcnkoZ2xtbmV0KQ0KfQ0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpsaWJyYXJ5KGNhcmV0KQ0KfQ0KaWYgKCFyZXF1aXJlKCJNQVNTIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKQ0KbGlicmFyeShNQVNTKQ0KfQ0KaWYgKCFyZXF1aXJlKCJtbGJlbmNoIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm1sYmVuY2giKQ0KbGlicmFyeShtbGJlbmNoKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwUk9DIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBST0MiKQ0KbGlicmFyeShwUk9DKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCmxpYnJhcnkocGxvdGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJwYW5kZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGFuZGVyIikNCmxpYnJhcnkocGFuZGVyKQ0KfQ0KIyMjIA0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BDQogICAgICAgICAgICAgICAgICAgICAgKSAgDQpgYGANCg0KXA0KDQojIEludHJvZHVjdGlvbg0KDQpSZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVsaW5nIGlzIGEgc29waGlzdGljYXRlZCBhcHByb2FjaCB0aGF0IGV4dGVuZHMgdHJhZGl0aW9uYWwgcmVncmVzc2lvbiB0ZWNobmlxdWVzIGJ5IGluY29ycG9yYXRpbmcgYSBwZW5hbHR5IHRlcm0gaW50byB0aGUgbG9zcyBmdW5jdGlvbi4gVGhpcyBwZW5hbHR5IHRlcm0gY29uc3RyYWlucyB0aGUgbWFnbml0dWRlIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cywgdGhlcmVieSBjb250cm9sbGluZyB0aGUgY29tcGxleGl0eSBvZiB0aGUgbW9kZWwgYW5kIG1pdGlnYXRpbmcgdGhlIHJpc2sgb2Ygb3ZlcmZpdHRpbmcuIFRoZSBtb3N0IGNvbW1vbiBmb3JtcyBvZiByZWd1bGFyaXplZCByZWdyZXNzaW9uIGluY2x1ZGUgUmlkZ2UgUmVncmVzc2lvbiwgd2hpY2ggYXBwbGllcyBhbiAkTF8yJCBwZW5hbHR5IHRvIHRoZSBzdW0gb2Ygc3F1YXJlZCBjb2VmZmljaWVudHM7IExBU1NPLCB3aGljaCB1c2VzIGFuICRMXzEkIHBlbmFsdHkgdG8gZW5jb3VyYWdlIHNwYXJzaXR5IGJ5IHNocmlua2luZyBzb21lIGNvZWZmaWNpZW50cyB0byB6ZXJvOyBhbmQgRWxhc3RpYyBOZXQsIHdoaWNoIGNvbWJpbmVzIGJvdGggJExfMSQgYW5kICRMXzIkIHBlbmFsdGllcyB0byBiYWxhbmNlIHRoZWlyIHJlc3BlY3RpdmUgYWR2YW50YWdlcy4gVGhlc2UgbWV0aG9kcyBhcmUgcGFydGljdWxhcmx5IHVzZWZ1bCBpbiBzY2VuYXJpb3Mgd2hlcmUgdHJhZGl0aW9uYWwgcmVncmVzc2lvbiBtb2RlbHMgc3RydWdnbGUsIHN1Y2ggYXMgd2hlbiBkZWFsaW5nIHdpdGggaGlnaC1kaW1lbnNpb25hbCBkYXRhIG9yIG11bHRpY29sbGluZWFyaXR5IGFtb25nIHByZWRpY3RvcnMuDQoNCg0KVGhlIHByaW1hcnkgb2JqZWN0aXZlcyBvZiByZWd1bGFyaXplZCByZWdyZXNzaW9uIGFyZSBtdWx0aWZhY2V0ZWQuIEZpcnN0IGFuZCBmb3JlbW9zdCwgaXQgYWltcyB0byBpbXByb3ZlIHRoZSBnZW5lcmFsaXphdGlvbiBhYmlsaXR5IG9mIHJlZ3Jlc3Npb24gbW9kZWxzIGJ5IHJlZHVjaW5nIG92ZXJmaXR0aW5nLiBUaGlzIGlzIGFjaGlldmVkIGJ5IHBlbmFsaXppbmcgbGFyZ2UgY29lZmZpY2llbnRzLCB3aGljaCBoZWxwcyB0aGUgbW9kZWwgZm9jdXMgb24gdGhlIG1vc3QgaW1wb3J0YW50IHBhdHRlcm5zIGluIHRoZSBkYXRhLiBTZWNvbmQsIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gc2Vla3MgdG8gaGFuZGxlIG11bHRpY29sbGluZWFyaXR5IGJ5IHN0YWJpbGl6aW5nIGNvZWZmaWNpZW50IGVzdGltYXRlcywgbWFraW5nIHRoZW0gbW9yZSByZWxpYWJsZSBldmVuIHdoZW4gcHJlZGljdG9ycyBhcmUgY29ycmVsYXRlZC4gVGhpcmQsIGl0IGZhY2lsaXRhdGVzIGZlYXR1cmUgc2VsZWN0aW9uLCBwYXJ0aWN1bGFybHkgd2l0aCBMQVNTTywgYnkgc2hyaW5raW5nIGxlc3MgaW1wb3J0YW50IGNvZWZmaWNpZW50cyB0byB6ZXJvLCB0aGVyZWJ5IHNpbXBsaWZ5aW5nIHRoZSBtb2RlbCBhbmQgaW1wcm92aW5nIGludGVycHJldGFiaWxpdHkuIEZpbmFsbHksIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gc3RyaWtlcyBhIGJhbGFuY2UgYmV0d2VlbiBiaWFzIGFuZCB2YXJpYW5jZSwgaW50cm9kdWNpbmcgYSBjb250cm9sbGVkIGFtb3VudCBvZiBiaWFzIHRvIHJlZHVjZSB2YXJpYW5jZSBhbmQgZW5oYW5jZSB0aGUgcm9idXN0bmVzcyBvZiB0aGUgbW9kZWwuDQoNClRoaXMgbm90ZSBpcyBkZWRpY2F0ZWQgdG8gcmVndWxhcml6ZWQgcmVncmVzc2lvbiBtb2RlbGluZyBtZXRob2RzLCB3aGljaCBoYXZlIGJlZW4gcmVjZW50bHkgZGV2ZWxvcGVkIGJ5IHN0YXRpc3RpY2lhbnMgYW5kIGFyZSBub3cgd2lkZWx5IGFkb3B0ZWQgaW4gdGhlIG1hY2hpbmUgbGVhcm5pbmcgY29tbXVuaXR5LiBUaGUgbWF0aGVtYXRpY2FsIGNvbnRlbnQgd2lsbCBiZSBrZXB0IHRvIGEgbWluaW11bSB0byBmYWNpbGl0YXRlIGEgaGlnaC1sZXZlbCB0ZWNobmljYWwgdW5kZXJzdGFuZGluZyBvZiB0aGVzZSBtZXRob2RzLg0KDQoNCiMgJExfcCQgTm9ybQ0KDQoNCk9uZSBvZiB0aGUga2V5IG1hdGhlbWF0aWNhbCB0ZXJtcyB1c2VkIGluIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbWV0aG9kcyBpcyB0aGUgJExfcCQgbm9ybSB3aGljaCBkZWZpbmVkIGFzIGEgKipkaXN0YW5jZSoqIGJldHdlZW4gdG8gcG9pbnRzLCAkXG1hdGhiZnt4fSQgYW5kICRcbWF0aGJme3l9JCBpbiBhICRkLSRkaW1lbnNpb25hbCB2ZWN0b3Igc3BhY2Ugd2l0aCB0aGUgZm9sbG93aW5nIGV4cHJlc3Npb24NCg0KJCQNCkxfcChcbWF0aGJme3h9IC0gXG1hdGhiZnt5fSkgPSBcbGVmdFsgXHN1bV97aj0xfV5kICh4X2ogLSB5X2opXnBccmlnaHRdXnsxL3B9IFxzdGFja3JlbHtcdGV4dHtkZWZpbmVkfX17XGVxdWl2fSB8fFxtYXRoYmZ7eH0gLSBcbWF0aGJme3l9fHxfcC4NCiQkDQpJbiB0aGUgdHdvIGRpbWVuc2lvbmFsIHZlY3RvciBzcGFjZSwgJFxtYXRoYmZ7eH0gPSAoeF8xLCB4XzIpJCBhbmQgJFxtYXRoYmZ7eX0gPSAoeV8xLCB5XzIpJCwgdGhlIHRoZSAkTF8yJCBub3JtIGlzIHNpbXBseSB0aGUgZm9sbG93aW5nIEV1Y2xpZGVhbiBkaXN0YW5jZQ0KDQokJA0KfHxcbWF0aGJme3h9IC0gXG1hdGhiZnt5fXx8XzIgPSBcbGVmdFsgKHhfMS15XzEpXjIgKyAoeF8yLXlfMileMlxyaWdodF1eezEvMn0gPSBcc3FydHsoeF8xLXlfMSleMiArICh4XzIteV8yKV4yfQ0KJCQNCg0KU2ltaWxhcmx5LCAkTF8xJCBub3JtIG9mIHRoZSBhYm92ZSB0d28gcG9pbnRzIGFyZSBnaXZlbiBieQ0KDQokJA0KfHxcbWF0aGJme3h9IC0gXG1hdGhiZnt5fXx8XzEgPSBcbGVmdFsgKHhfMS15XzEpXjEgKyAoeF8yLXlfMileMVxyaWdodF1eezEvMX0gPSB8eF8xLXlfMXwgKyB8eF8yLXlfMnwNCiQkDQpUaGUgY29ycmVzcG9uZGluZyAkTF97MS8yfSQgbm9ybSBpcyBnaXZlbiBieQ0KDQokJA0KfHxcbWF0aGJme3h9IC0gXG1hdGhiZnt5fXx8X3sxLzJ9ID0gXGxlZnRbICh4XzEteV8xKV57MS8yfSArICh4XzIteV8yKV57MS8yfVxyaWdodF1eezJ9ID0gXGxlZnQoIFxzcXJ0e3hfMS15XzF9ICsgXHNxcnR7eF8yLXlfMn1ccmlnaHQpXjIsDQokJA0KDQpUaGUgZm9sbG93aW5nIGZpZ3VyZSBzaG93cyB0aGUgZ2VvbWV0cnkgb2YgdmFyaW91cyAkTF9wJCBub3JtcyBpbmNsdWRpbmcgdGhlIGFib3ZlIHRoZQ0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI0MCUiLCBmaWcuY2FwID0gIlRoZSBnZW9tZXRyeSBvZiAkTF9wJCBub3JtIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9McE5vcm0ucG5nIikNCmBgYA0KDQoNClwNCg0KIyBHZW5lcmFsIEZyYW1ld29yayBvZiBSZWd1bGFyaXphdGlvbg0KDQpSZWNhbGwgdGhhdCB0aGUgZXF1YXRpb25zIGZvciBsaW5lYXIgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIHdpdGggJGskIGZlYXR1cmUgdmFyaWFibGVzIGFyZSBnaXZlbiBieSwgcmVzcGVjdGl2ZWx5DQoNCiQkDQp5ID0gXGFscGhhXzAgKyBcYWxwaGFfMSB4XzEgKyBcYWxwaGFfMiB4XzIgKyBcY2RvdHMgKyBcYWxwaGFfayB4X2sNCiQkDQphbmQgDQoNCiQkDQpQKFk9MSkgPSBcZnJhY3tcZXhwKFxiZXRhXzAgKyBcYmV0YV8xIHhfMSArIFxjZG90cyArIFxiZXRhX2sgeF9rKX17MSArIFxleHAoXGJldGFfMCArIFxiZXRhXzEgeF8xICsgXGNkb3RzICsgXGJldGFfayB4X2spfSBcc3RhY2tyZWx7XHRleHR7ZGVmaW5lZH19e1xlcXVpdn0gcA0KJCQNCg0KVGhlICoqbG9zcyBmdW5jdGlvbioqIChpLmUuLCBsaWtlbGlob29kIGZ1bmN0aW9uKSB1c2VkIHRvIGVzdGltYXRlIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBvZiBsaW5lYXIgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIGFyZSBnaXZlbiBieSByZXNwZWN0aXZlbHkNCg0KJCQNClx0ZXh0e0xPU1N9X3tcdGV4dHtsaW5lYXJ9fSA9IFxzdW1fe2k9MX1ebiAoeV9pIC0gXGhhdHt5fV9pKV4yDQokJA0KDQphbmQNCg0KJCQNClx0ZXh0e0xPU1N9X3tcdGV4dHtsb2dpc3RpY319ID0gLVxmcmFjezF9e259XHN1bV97aT0xfV5uIFxsZWZ0WyAgeV9pXGxvZyhcaGF0e3B9X2kpICsgKDEteV9pKVxsb2coMS1caGF0e3B9X2kpIFxyaWdodF0uDQokJA0KDQpUaGUgKip3b3JraW5nIGxvc3MgZnVuY3Rpb25zKiogb2YgY29tbW9ubHkgdXNlZCByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVscyBhcmUgZ2l2ZW4gYnkNCg0KKiAqKkwxIFJlZ3VsYXJpemF0aW9uIChMYXNzbyBSZWdyZXNzaW9uKSoqOiBBZGRzIHRoZSBhYnNvbHV0ZSB2YWx1ZSBvZiB0aGUgY29lZmZpY2llbnRzIGFzIGEgcGVuYWx0eSB0ZXJtIHRvIHRoZSBsb3NzIGZ1bmN0aW9uIHRoYXQgPGZvbnQgY29sb3IgPSAicmVkIj4qKnNocmlua3Mgc29tZSBjb2VmZmljaWVudHMgdG8gemVybyoqPC9mb250PiwgZWZmZWN0aXZlbHkgcGVyZm9ybWluZyBmZWF0dXJlIHNlbGVjdGlvbi4gVGhlIGFjdHVhbCBsb3NzIGZ1bmN0aW9uIG9mIHRoZSBjb3JyZXNwb25kaW5nICRMXzEkLXJlZ3VsYXJpemVkIGxpbmVhciBhbmQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgYXJlIGdpdmVuIGJ5IHJlc3BlY3RpdmVseQ0KDQokJA0KXHRleHR7TEFTU099X3tcdGV4dHtsaW5lYXJ9fSA9IFx0ZXh0e0xPU1N9X3tcdGV4dHtsaW5lYXJ9fSArIFxsYW1iZGFcc3VtX3tqPTF9XmsgfFxhbHBoYV9qfCBcIFwgXCBcdGV4dHthbmR9IFwgXCBcIFx0ZXh0e0xBU1NPfV97XHRleHR7bG9naXN0aWN9fSA9IFx0ZXh0e0xPU1N9X3tcdGV4dHtsb2dpc3RpY319ICsgXGdhbW1hXHN1bV97aj0xfV5rIHxcYmV0YV9qfCANCiQkDQoNClRoZSBmb2xsb3dpbmcgZ2VvbWV0cnkgb2YgdGhlIExBU1NPIGV4cGxhaW5zIHdoeSBpdCByZWR1Y2VzIGRpbWVuc2lvbiBvZiB0aGUgZmVhdHVyZSBzcGFjZSAoaS5lLiwgaXQgZHJvcHMgZmVhdHVyZSB2YXJpYWJsZXMgbWF0aGVtYXRpY2FsbHkpDQoNCg0KKiAqKkwyIFJlZ3VsYXJpemF0aW9uIChSaWRnZSBSZWdyZXNzaW9uKSoqOiBBZGRzIHRoZSBzcXVhcmVkIG1hZ25pdHVkZSBvZiB0aGUgY29lZmZpY2llbnRzIGFzIGEgcGVuYWx0eSB0ZXJtIHRvIHRoZSBsb3NzIGZ1bmN0aW9uIHRoYXQgU2hyaW5rcyBhbGwgY29lZmZpY2llbnRzIDxmb250IGNvbG9yID0gInJlZCI+ICoqYnV0IGRvZXMgbm90IHNldCB0aGVtIHRvIHplcm8qKjwvZm9udD4sIHJlZHVjaW5nIHRoZWlyIGltcGFjdC4gVGhlIGxvc3MgZnVuY3Rpb25zIG9mIHRoZSByZWd1bGFyaXplZCBsaW5lYXIgYW5kIGxvZ2lzdGljIG1vZGVscyBhcmUgZ2l2ZW4gYnkNCg0KJCQNClx0ZXh0e0xBU1NPfV97XHRleHR7bGluZWFyfX0gPSBcdGV4dHtMT1NTfV97XHRleHR7bGluZWFyfX0gKyBcbGFtYmRhXHN1bV97aj0xfV5rIHxcYWxwaGFfanxeMiBcIFwgXCBcdGV4dHthbmR9IFwgXCBcIFx0ZXh0e0xBU1NPfV97XHRleHR7bG9naXN0aWN9fSA9IFx0ZXh0e0xPU1N9X3tcdGV4dHtsb2dpc3RpY319ICsgXGdhbW1hXHN1bV97aj0xfV5rIHxcYmV0YV9qfF4yIA0KJCQNCg0KKiAqKkxwIFJlZ3VsYXJpemF0aW9uICgkMTwgcCA8MiQpIEVsYXN0aWMgTmV0Kio6IENvbWJpbmVzIEwxIGFuZCBMMiByZWd1bGFyaXphdGlvbi4gVGhlIGxvc3MgZnVuY3Rpb25zIG9mIHRoZSByZWd1bGFyaXplZCBsaW5lYXIgYW5kIGxvZ2lzdGljIG1vZGVscyBhcmUgZ2l2ZW4gYnkgcmVzcGVjdGl2ZWx5DQoNCiQkDQpcdGV4dHtMQVNTT31fe1x0ZXh0e2xpbmVhcn19ID0gXHRleHR7TE9TU31fe1x0ZXh0e2xpbmVhcn19KyBcbGFtYmRhXzFcc3VtX3tqPTF9XmsgfFxhbHBoYV9qfCArIFxsYW1iZGFfMlxzdW1fe2o9MX1eayB8XGFscGhhX2p8XjIgDQokJA0KDQphbmQNCg0KJCQNClx0ZXh0e0xBU1NPfV97XHRleHR7bG9naXN0aWN9fSA9IFx0ZXh0e0xPU1N9X3tcdGV4dHtsb2dpc3RpY319ICsgXGdhbW1hXzFcc3VtX3tqPTF9XmsgfFxiZXRhX2p8ICArIFxnYW1tYV8yXHN1bV97aj0xfV5rIHxcYmV0YV9qfF4yIA0KJCQNCg0KVGhlIGdlb21ldHJ5IG9mIHRoZSBhYm92ZSByZWd1bGFyaXplZCByZWdyZXNzaW9uIGlzIGRlcGljdGVkIGluIGEgMi1kaW1lbnNpb25hbCBmZWF0dXJlIHNwYWNlLg0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI5OSUiLCBmaWcuY2FwPSJUaGUgZ2VvbWV0cnkgb2YgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBpbiAyLWRpbWVuc2lvbmFsIGZlYXR1cmUgc3BhY2UuIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9HZW9tZXRyeU9mUmVndWxhcml6ZWRFc3RpbWF0ZXMucG5nIikNCmBgYA0KDQpUaGUgYWJvdmUgY29udG91ciBwbG90cyAoc2hvd24gaW4gYmx1ZSkgcmVwcmVzZW50IHRoZSBzdXJmYWNlIG9mIHRoZSBvcmlnaW5hbCBsb3NzIGZ1bmN0aW9ucywgd2hpY2ggZG9lcyBoYXZlIHRoZSByZWd1bGFyaXphdGlvbiBwZW5hbHR5IHRlcm0uIFRoZSBjb29yZGluYXRlcyBvZiB0aGUgY2VudGVyIHBvaW50cyBjb3JyZXNwb25kIHRvIHRoZSBlc3RpbWF0ZXMgb2YgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLiBUaGUgY29vcmRpbmF0ZXMgb2YgdGhlIGludGVyc2VjdGlvbiBiZXR3ZWVuIHRoZSBjb250b3VycyBvZiB0aGUgb3JpZ2luYWwgbG9zcyBmdW5jdGlvbiBhbmQgdGhlIHJlZ3VsYXJpemVkIGxvc3MgZnVuY3Rpb24gcmVwcmVzZW50IHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnRzIG9mIHRoZSB1bmRlcmx5aW5nIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbW9kZWwuDQoNCg0KPGZvbnQgY29sb3IgPSAicmVkIj4qKkltcG9ydGFudCBSZW1hcmsqKjwvZm9udD4gKipSZWd1bGFyaXphdGlvbiBUZWNobmlxdWVzKiogc3VjaCBhcyBgTEFTU09gLCBgUmlkZ2VgLCBhbmQgYEVsYXN0aWNuZXRgIGFyZSBvbmx5ICBkaWZmZXJlbnQgbWV0aG9kcyBmb3IgZXN0aW1hdGluZyB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgKHNvbWUgb2YgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIG9yaWdpbmFsIG1vZGVsIG1heSBiZSBkcm9wcGVkIGFmdGVyIHBlcmZvcm1lZCBzb21lIHJlZ3VsYXJpemF0aW9uIHRlY2huaXF1ZXMpLiBJbiB0aGUgbmV4dCBzZWN0aW9uLCB3ZSB1c2Ugc2hvdyB0aGUgcmVzdWx0aW5nIHJlZ3VsYXJpemVkIG1vZGVscyB3aXRoIGVzdGltYXRlZCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyAgDQoNCg0KXA0KDQojIFJlZ3VsYXJpemF0aW9uIHdpdGggUiBgZ2xtbmV0YA0KDQoNClRoZSBSIGxpYnJhcnkgYGdsbW5ldGAgaXMgcHJpbWFyaWx5IGRlc2lnbmVkIGZvciBpbXBsZW1lbnRpbmcgcmVndWxhcml6YXRpb24gdGVjaG5pcXVlcy4gSXQgaXMgb2Z0ZW4gdXNlZCBpbiBjb25qdW5jdGlvbiB3aXRoIHRoZSBgY2FyZXRgIGxpYnJhcnkgdG8gYnVpbGQgYW5kIGV2YWx1YXRlIHZhcmlvdXMgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBtb2RlbHMuIEZyb20gYSBtYXRoZW1hdGljYWwgcGVyc3BlY3RpdmUsIHRoZSBvcHRpbWl6YXRpb24gcHJvYmxlbSBhc3NvY2lhdGVkIHdpdGggcmVndWxhcml6ZWQgbG9zcyBmdW5jdGlvbnMsIHdoaWNoIHlpZWxkIGVzdGltYXRlcyBvZiByZWdyZXNzaW9uIGNvZWZmaWNpZW50cywgY2FuIGJlIHZpZXdlZCBhcyBhIGNvbnN0cmFpbmVkIG1heGltaXphdGlvbiBwcm9ibGVtIGludm9sdmluZyBhIExhZ3JhbmdlIG11bHRpcGxpZXIsIGRlbm90ZWQgYXMgJFxsYW1iZGEkLiBUaGlzIExhZ3JhbmdlIG11bHRpcGxpZXIgJFxsYW1iZGEkIGFjdHMgYXMgYSBoeXBlcnBhcmFtZXRlciBhbmQgbXVzdCBiZSBjYXJlZnVsbHkgdHVuZWQsIHR5cGljYWxseSB0aHJvdWdoIGNyb3NzLXZhbGlkYXRpb24sIHRvIGFjaGlldmUgb3B0aW1hbCBtb2RlbCBwZXJmb3JtYW5jZS4NCg0KDQpgZ2xtbmV0YCBpbnRyb2R1Y2VzIGEgbWl4aW5nIHBhcmFtZXRlciAkXGFscGhhJCB0byBmb3JtdWxhdGUgYSB1bmlmaWVkIGZyYW1ld29yayBpbiB0aGUgZm9sbG93aW5nIGZvcm0NCg0KJCQNClx0ZXh0e0xPU1N9X3tcdGV4dHtyZWd1bGFyaXphdGlvbn19ID0gXHRleHR7TE9TU31fe1x0ZXh0e29yaWdpbmFsfX0gKyBcbGFtYmRhIFxsZWZ0WyBcZnJhY3soMS1cYWxwaGEpfXsyfXx8XGJldGF8fF8yXjIgKyBcYWxwaGEgfHxcYmV0YXx8XzEuXHJpZ2h0XQ0KJCQNCg0KVGhhdCBpcywgJFxhbHBoYSA9IDEkIHlpZWxkcyB0aGUgTEFTU08gcmVncmVzc2lvbiBhbmQgJFxhbHBoYSA9IDAkIHlpZWxkcyB0aGUgcmlkZ2UgcmVncmVzc2lvbi4gQWxsIG90aGVyIHZhbHVlcyBvZiAkMCBcbGUgXGFscGhhIFxsZSAxJCByZXByZXNlbnRzIHRoZSAqKmVsYXN0aWMgbmV0KiogcmVndWxhcml6ZWQgcmVncmVzc2lvbi4NCg0KDQoNCg0KIyBSZWd1bGFyaXplZCBMaW5lYXIgUmVncmVzc2lvbg0KDQpUaGlzIHNlY3Rpb24gdXRpbGl6ZXMgdGhlIHdpZGVseS11c2VkIEJvc3RvbiBIb3VzaW5nIGRhdGEgc2V0IChhdmFpbGFibGUgaW4gdGhlIFIgbGlicmFyeSBgTUFTU2ApIHRvIGRlbW9uc3RyYXRlIGhvdyByZWd1bGFyaXphdGlvbiBsaW5lYXIgcmVncmVzc2lvbiBjYW4gYmUgYXBwbGllZCB0byBwcmVkaWN0IGhvdXNlIHByaWNlcyBpbiBSLiANCg0KVGhpcyBkYXRhc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIGNvbGxlY3RlZCBieSB0aGUgVS5TIENlbnN1cyBTZXJ2aWNlIGNvbmNlcm5pbmcgaG91c2luZyBpbiB0aGUgYXJlYSBvZiBCb3N0b24gTWFzcy4gSXQgd2FzIG9idGFpbmVkIGZyb20gdGhlIFN0YXRMaWIgYXJjaGl2ZSAoaHR0cDovL2xpYi5zdGF0LmNtdS5lZHUvZGF0YXNldHMvYm9zdG9uKSwgYW5kIGhhcyBiZWVuIHVzZWQgZXh0ZW5zaXZlbHkgdGhyb3VnaG91dCB0aGUgbGl0ZXJhdHVyZSB0byBiZW5jaG1hcmsgYWxnb3JpdGhtcy4gSG93ZXZlciwgdGhlc2UgY29tcGFyaXNvbnMgd2VyZSBwcmltYXJpbHkgZG9uZSBvdXRzaWRlIG9mIERlbHZlIGFuZCBhcmUgdGh1cyBzb21ld2hhdCBzdXNwZWN0LiBUaGUgZGF0YXNldCBpcyBzbWFsbCBpbiBzaXplIHdpdGggb25seSA1MDYgY2FzZXMuIEEgY29weSBvZiB0aGUgZGF0YSBzZXQgY2FuIGJlIGZvdW5kIGF0OiA8aHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUyL3cwNy9Cb3N0b25Ib3VzaW5nLmNzdj4NCg0KKiBDUklNIC0gcGVyIGNhcGl0YSBjcmltZSByYXRlIGJ5IHRvd24NCiogWk4gLSBwcm9wb3J0aW9uIG9mIHJlc2lkZW50aWFsIGxhbmQgem9uZWQgZm9yIGxvdHMgb3ZlciAyNSwwMDAgc3EuZnQuDQoqIElORFVTIC0gcHJvcG9ydGlvbiBvZiBub24tcmV0YWlsIGJ1c2luZXNzIGFjcmVzIHBlciB0b3duLg0KKiBDSEFTIC0gQ2hhcmxlcyBSaXZlciBkdW1teSB2YXJpYWJsZSAoMSBpZiB0cmFjdCBib3VuZHMgcml2ZXI7IDAgb3RoZXJ3aXNlKQ0KKiBOT1ggLSBuaXRyaWMgb3hpZGVzIGNvbmNlbnRyYXRpb24gKHBhcnRzIHBlciAxMCBtaWxsaW9uKQ0KKiBSTSAtIGF2ZXJhZ2UgbnVtYmVyIG9mIHJvb21zIHBlciBkd2VsbGluZw0KKiBBR0UgLSBwcm9wb3J0aW9uIG9mIG93bmVyLW9jY3VwaWVkIHVuaXRzIGJ1aWx0IHByaW9yIHRvIDE5NDANCiogRElTIC0gd2VpZ2h0ZWQgZGlzdGFuY2VzIHRvIGZpdmUgQm9zdG9uIGVtcGxveW1lbnQgY2VudHJlcw0KKiBSQUQgLSBpbmRleCBvZiBhY2Nlc3NpYmlsaXR5IHRvIHJhZGlhbCBoaWdod2F5cw0KKiBUQVggLSBmdWxsLXZhbHVlIHByb3BlcnR5LXRheCByYXRlIHBlciAkMTAsMDAwDQoqIFBUUkFUSU8gLSBwdXBpbC10ZWFjaGVyIHJhdGlvIGJ5IHRvd24NCiogQiAtIDEwMDAoQmsgLSAwLjYzKV4yIHdoZXJlIEJrIGlzIHRoZSBwcm9wb3J0aW9uIG9mIGJsYWNrcyBieSB0b3duDQoqIExTVEFUIC0gJSBsb3dlciBzdGF0dXMgb2YgdGhlIHBvcHVsYXRpb24NCiogTUVEViAtIE1lZGlhbiB2YWx1ZSBvZiBvd25lci1vY2N1cGllZCBob21lcyBpbiAkMTAwMCdzDQoNCg0KRm9yIHNpbXBsaWNpdHksIHdlIGxldmVyYWdlIGZ1bmN0aW9ucyBmcm9tIHR3byBwb3B1bGFyIFIgbGlicmFyaWVzLCBgZ2xtbmV0YCBhbmQgYGNhcmV0YCwgdG8gaW1wbGVtZW50IHRoZSByZWd1bGFyaXphdGlvbiB0ZWNobmlxdWVzLg0KDQoNCiMjIE1vZGVsaW5nIFN0ZXBzDQoNClRoZSBmb2xsb3dpbmcgYXJlIHRoZSBiYXNpYyBzdGVwcyBpbiBpbiB0aGUgaW1wbGVtZW50YXRpb24NCg0KKiBTZXR0aW5nIHNlZWQgdG8gZ3VhcmFudGVlIHJlcHJvZHVjaWJpbGl0eQ0KKiBSYW5kb20gRGF0YSBTcGxpdHRpbmcgZm9yIHRyYWluaW5nLCBjcm9zcy12YWxpZGF0aW9uLCBhbmQgdGVzdGluZw0KKiBTdGFuZGFyZGl6aW5nIGZlYXR1cmUgdmFyaWFibGVzIDxmb250IGNvbG9yID0gInJlZCI+LSB0aGlzIGlzIGV4dHJlbWVseSBpbXBvcnRhbnQgaW4gcmVndWxhcml6YXRpb24hPC9mb250Pg0KKiBidWlsZGluZyByZWd1bGFyaXplZCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMNCiogUHJlZGljdGluZyB0aGUgcmVzcG9uc2Ugd2l0aCB0aGUgdGVzdGluZyBkYXRhIHNldA0KKiBFdmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2UgbWVhc3VyZTogcm9vdC1tZWFuLXNxdWFyZWQtZXJyb3IgKFJNU0UpDQoqIGZpbmRpbmcgdGhlIG9wdGltYWwgTGFncmFuZ2UgbXVsdGlwbGllcnMgdGhyb3VnaCBjcm9zcy12YWxpZGF0aW9uDQoqIHJlcG9ydGluZyB0aGUgZmluYWwgcmVzdWx0aW5nIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbW9kZWxzICh3aXRoIGVzdGltYXRlZCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cykNCg0KSW4gbmV4dCBmZXcgc3Vic2VjdGlvbnMsIHdlIGludHJvZHVjZSB0aGUgbWFqb3Igc3RlcHMgdG8gYmUgY29uc2lkZXJlZCBpbiBidWlsZGluZyByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVscy4NCg0KIyMjIENvZWZmaWNpZW50IFBhdGggQW5hbHlzaXMNCg0KJFxsYW1iZGEkIGlzIHRoZSByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXIgaW4gYGdsbW5ldCgpYCB0aGF0IGNvbnRyb2xzIHRoZSBzdHJlbmd0aCBvZiB0aGUgcGVuYWx0eSBhcHBsaWVkIHRvIHRoZSBjb2VmZmljaWVudHMuIEEgbGFyZ2VyICRcbGFtYmRhJCBzaHJpbmtzIHRoZSBjb2VmZmljaWVudHMgbW9yZSBhZ2dyZXNzaXZlbHksIHBvdGVudGlhbGx5IHJlZHVjaW5nIG92ZXJmaXR0aW5nIGJ1dCBpbmNyZWFzaW5nIGJpYXMuIEEgc21hbGxlciAkXGxhbWJkYSQgYWxsb3dzIHRoZSBtb2RlbCB0byBmaXQgdGhlIGRhdGEgbW9yZSBjbG9zZWx5IGJ1dCBtYXkgbGVhZCB0byBvdmVyZml0dGluZy4NCg0KV2UgZmlyc3QgZXhhbWluZSBob3cgdGhlIGh5cGVycGFyYW1ldGVyICRcbGFtYmRhJCBwZW5hbGl6ZXMgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGFuZCBhZmZlY3RzIG1vZGVsIGZpdC4gQ29lZmZpY2llbnQgcGF0aCBhbmFseXNpcyBhbmQgdGhlIHBsb3Qgb2YgdGhlIGdvb2RuZXNzLW9mLWZpdCBtZWFzdXJlIChSTVNFKSBhcmUgY29tbW9ubHkgdXNlZCB2aXN1YWwgdG9vbHMgZm9yIG1vZGVsIHNlbGVjdGlvbi4NCg0KDQpgYGB7cn0NCnNldC5zZWVkKDExMjIzMykgICAgIyByZW1vdmUgdGhlIHNlZWQgYnkgdXNpbmcgIHNldC5zZWVkKE5VTEwpDQoNCiMgTG9hZCB0aGUgZGF0YXNldA0KZGF0YSgiQm9zdG9uIikNClggPC0gYXMubWF0cml4KEJvc3RvblssIC0xNF0pICAjIEZlYXR1cmVzIChhbGwgY29sdW1ucyBleGNlcHQgdGhlIHRhcmdldCkNCnkgPC0gQm9zdG9uJG1lZHYgICMgVGFyZ2V0IHZhcmlhYmxlIChtZWRpYW4gaG91c2UgdmFsdWUpDQoNCiMgU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzDQp0cmFpbl9pbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHksIHAgPSAwLjgsIGxpc3QgPSBGQUxTRSkNClhfdHJhaW4gPC0gWFt0cmFpbl9pbmRleCwgXQ0KWF90ZXN0IDwtIFhbLXRyYWluX2luZGV4LCBdDQp5X3RyYWluIDwtIHlbdHJhaW5faW5kZXhdDQp5X3Rlc3QgPC0geVstdHJhaW5faW5kZXhdDQoNCiMgU3RhbmRhcmRpemUgdGhlIGRhdGEgKGltcG9ydGFudCBmb3IgcmVndWxhcml6YXRpb24pDQpwcmVwcm9jZXNzX3BhcmFtcyA8LSBwcmVQcm9jZXNzKFhfdHJhaW4sIG1ldGhvZCA9IGMoImNlbnRlciIsICJzY2FsZSIpKQ0KWF90cmFpbiA8LSBwcmVkaWN0KHByZXByb2Nlc3NfcGFyYW1zLCBYX3RyYWluKQ0KWF90ZXN0IDwtIHByZWRpY3QocHJlcHJvY2Vzc19wYXJhbXMsIFhfdGVzdCkNCg0KIyMgZml0dGluZyB0aGUgbW9kZWwNCmZpdF9sYXNzbzwtIGdsbW5ldChYX3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgICB5X3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEpICAgICAgICAjIGxhc3NvIHJlZ3Jlc3Npb24gDQpmaXRfcmlkZ2UgPC0gZ2xtbmV0KFhfdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgICB5X3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwKSAgICAgICAgICAjIFJpZGdlIHJlZ3Jlc3Npb24NCmZpdF9lbGFzdGljX25ldCA8LSBnbG1uZXQoWF90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHlfdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgICMgZWxhc3RpYyBuZXQNCg0KIyMgY3Jvc3MtdmFsaWRhdGlvbg0KY3ZfbGFzc28gPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMSkgICAjIGxhc3NvIHJlZ3Jlc3Npb24NCmN2X3JpZGdlIDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDApICAgIyBSaWRnZSByZWdyZXNzaW9uDQpjdl9lbGFzdGljX25ldCA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwLjUpICAjIGVsYXN0aWMgbmV0DQpgYGANCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD02fQ0KcGFyKG1hcj1jKDUsNCw2LDMpKQ0KIyBQbG90IGNvZWZmaWNpZW50IHBhdGgNCnBsb3QoZml0X2xhc3NvLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsID0gVFJVRSwNCiAgICAgbHdkID0gMS41LA0KICAgICBtYWluID0gIkNvZWZmaWNpZW50IFBhdGggQW5hbHlzaXM6IExBU1NPIiwNCiAgICAgY2V4Lm1haW4gPSAwLjksDQogICAgIGNvbCA9IHJhaW5ib3coMTApKQ0KYWJsaW5lKHYgPSAxLCBjb2wgPSAicHVycGxlIiwgbHR5ID0gNCwgbHdkID0gMikNCmFibGluZSh2ID0gLTEsIGNvbCA9ICJzdGVlbGJsdWUiLCBsdHkgPSAyLCBsd2QgPSAyKQ0KYGBgDQoNCg0KVGhlIGNvZWZmaWNpZW50IHBhdGggcGxvdCBpbGx1c3RyYXRlcyBob3cgdGhlIHZhbHVlIG9mICRcbGFtYmRhJCBhZmZlY3RzIHRoZSBkZWdyZWUgb2Ygc2hyaW5rYWdlIGFwcGxpZWQgdG8gaW5kaXZpZHVhbCBjb2VmZmljaWVudHMuIFRoZSBudW1iZXJzIGF0IHRoZSB0b3Agb2YgdGhlIHBsb3QgaW5kaWNhdGUgdGhlIG51bWJlciBvZiBmZWF0dXJlIHZhcmlhYmxlcyByZXRhaW5lZCBpbiB0aGUgbW9kZWwgZm9yIGEgZ2l2ZW4gY2hvaWNlIG9mICRcbGFtYmRhJC4gRm9yIGluc3RhbmNlLCB3aGVuICRcbG9nKFxsYW1iZGEpPTEkLCB0aHJlZSB2YXJpYWJsZXMgcmVtYWluIGluIHRoZSBtb2RlbC4gQnkgdHJhY2luZyB0aGUgcGF0aHMsIHdlIGNhbiBpZGVudGlmeSB0aGUgSURzIG9mIHRoZXNlIHRocmVlIGZlYXR1cmUgdmFyaWFibGVzIChpLmUuLCA2LCAxMSwgYW5kIDEzKS4gVGhlIG5hbWVzIG9mIHRoZXNlIHZhcmlhYmxlcyBjYW4gYmUgcmV0cmlldmVkIHVzaW5nIHRoZSBSIGNvbW1hbmQgYGNvbG5hbWVzKFhfdHJhaW4pW2MoNiwxMSwxMyldYCwgd2hpY2ggeWllbGRzIGBybWAsIGBwdHJhdGlvYCwgYW5kIGBsc3RhdGAuDQoNClNpbWlsYXJseSwgd2hlbiAkbG9nKFxsYW1iZGEpPSAtMSQsIG5pbmUgZmVhdHVyZSB2YXJpYWJsZXMgYXJlIHJldGFpbmVkIGluIHRoZSBtb2RlbCwgd2l0aCB2YXJpYWJsZSBJRHM6IDYsIDEyLCA0LCAzLCAxLCA1LCA4LCAxMSwgYW5kIDEzLiBUaGUgY29ycmVzcG9uZGluZyB2YXJpYWJsZSBuYW1lcyBjYW4gYmUgZm91bmQgdXNpbmcgdGhlIGNvbW1hbmQgYGNvbG5hbWVzKFhfdHJhaW4pW2MoNiwxMiw0LDMsMSw1LDgsMTEsMTMpXWAsIHdoaWNoIHJldHVybnMgYHJtYCwgYGJsYWNrYCwgYGNoYXNgLCBgaW5kdXNgLCBgY3JpbWAsIGBub3hgLCBgZGlzYCwgYHB0cmF0aW9gLCBhbmQgYGxzdGF0YC4NCg0KQWRkaXRpb25hbGx5LCB0aGUgcGxvdCBkZW1vbnN0cmF0ZXMgdGhhdCBhcyAkXGxhbWJkYSQgaW5jcmVhc2VzLCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgc2hyaW5rLCBhbmQgc29tZSBvZiB0aGVtIGNvbnZlcmdlIHRvIHplcm8uIFRoaXMgaW5kaWNhdGVzIHRoYXQgY2VydGFpbiBmZWF0dXJlIHZhcmlhYmxlcyBhcmUgZHJvcHBlZCBmcm9tIHRoZSBtb2RlbCBhcyAkXGxhbWJkYSQgYmVjb21lcyBsYXJnZXIuDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD00fQ0KcGFyKG1hcj1jKDUsNCw2LDMpKQ0KIyMNCnBsb3QoY3ZfbGFzc28sIG1haW4gPSAiUk1TRSBQbG90OiBMQVNTTyIsDQogICAgIGNleC5tYWluID0gMC45KQ0KYGBgDQoNClRoZSBhYm92ZSBwZXJmb3JtYW5jZSBwbG90IHNob3dzIHRoYXQgYXMgJFxsYW1iZGEkIGluY3JlYXNlcywgdGhlIE1TRSBpbmNyZWFzZXMuIFRoZSB0d28gdmVydGljYWwgbGluZXMgZ2l2ZSB0aGUgcmVmZXJlbmNlIG9mIHRoZSBjaG9pY2Ugb2YgJFxsYW1iZGEkLiBGb3IgYXZvaWQgb3ZlcmZpdHRpbmcgYW5kIHVuZGVyZml0dGluZywgJFxsb2coXGxhbWJkYSkkIHNob3VsZCBiZSBiZXR3ZWVuIHRoZSB0d28gdmVydGljYWwgbGluZXMuICAgDQoNClRoZSBwZXJmb3JtYW5jZSBwbG90IGFib3ZlIHNob3dzIHRoYXQgYXMgJFxsYW1iZGEkIGluY3JlYXNlcywgdGhlIE1TRSBhbHNvIGluY3JlYXNlcy4gVGhlIHR3byB2ZXJ0aWNhbCBsaW5lcyBpbmRpY2F0ZSByZWZlcmVuY2UgcG9pbnRzIGZvciBzZWxlY3RpbmcgJFxsYW1iZGEkLiBUbyBhdm9pZCBvdmVyZml0dGluZyBhbmQgdW5kZXJmaXR0aW5nLCAkXGxvZyhcbGFtYmRhKSQgc2hvdWxkIGxpZSBiZXR3ZWVuIHRoZXNlIHR3byBsaW5lcy4NCg0KDQojIyBUdW5pbmcgUmVndWxhcml6YXRpb24gUGFyYW1ldGVyDQoNClRoZSB0d28gdmFsdWVzIG9mICRcbGFtYmRhJCBjb3JyZXNwb25kaW5nIHRvIHRoZSB0d28gdmVydGljYWwgbGluZXMgb24gdGhlIGFib3ZlIHBsb3Qgb2YgZ29vZG5lc3Mtb2YtZml0IG1lYXN1cmUgd2FzIGNhbGN1bGF0ZWQgYmFzZWQgb24gdGhlIGNyb3NzIHZhbGlkYXRpb24uIEluIGBnbG1uZXQoKWAsIDEwLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiB3YXMgdXNlZCB0byB0dW5lICRcbGFtYmRhJCBpbiBgY3YuZ2xtbmV0KClgLiBUaGlzIHByb2Nlc3MgaXMgcmVwZWF0ZWQgZm9yIGVhY2ggZm9sZCwgYW5kIHRoZSBhdmVyYWdlIHByZWRpY3Rpb24gZXJyb3IgKGUuZy4sIG1lYW4gc3F1YXJlZCBlcnJvciBmb3IgcmVncmVzc2lvbiBvciBkZXZpYW5jZSBmb3IgY2xhc3NpZmljYXRpb24pIGlzIGNvbXB1dGVkIGZvciBlYWNoIGxhbWJkYS4gV2l0aCB0aGUgMTAtY3Jvc3MtdmFsaWRhdGlvbiwgYGN2LmdsbW5ldCgpYCByZXR1cm5zDQoNCiogYGxhbWJkYWA6IFRoZSBzZXF1ZW5jZSBvZiBsYW1iZGEgdmFsdWVzIHRlc3RlZC4NCg0KKiBgY3ZtYDogVGhlIG1lYW4gY3Jvc3MtdmFsaWRhdGVkIGVycm9yIGZvciBlYWNoIGxhbWJkYS4NCg0KKiBgY3ZzZGA6IFRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgY3Jvc3MtdmFsaWRhdGVkIGVycm9yLg0KDQoqIGBsYW1iZGEubWluYDogVGhlIHZhbHVlIG9mIGxhbWJkYSB0aGF0IGdpdmVzIHRoZSBtaW5pbXVtIGNyb3NzLXZhbGlkYXRlZCBlcnJvci4NCg0KKiBgbGFtYmRhLjFzZWA6IFRoZSBsYXJnZXN0IHZhbHVlIG9mIGxhbWJkYSBzdWNoIHRoYXQgdGhlIGVycm9yIGlzIHdpdGhpbiAxIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtaW5pbXVtLiBUaGlzIGlzIG9mdGVuIHVzZWQgdG8gc2VsZWN0IGEgbW9yZSBwYXJzaW1vbmlvdXMgbW9kZWwuDQoNClRoZSB0d28gdmVydGljYWwgbGluZXMgb24gdGhlIGFib3ZlIHBlcmZvcm1hbmNlIHBsb3QgY29ycmVzcG9uZCB0byBgbGFtYmRhLm1pbmAgYW5kIGBsYW1iZGEuMXNlYCwgcmVzcGVjdGl2ZWx5Lg0KDQoNCmBgYHtyfQ0KIyBDcm9zcy12YWxpZGF0aW9uIHRvIGZpbmQgdGhlIGJlc3QgbGFtYmRhDQpjdl9sYXNzbyA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAxKQ0KY3ZfcmlkZ2UgPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCkNCmN2X2VsYXN0aWNfbmV0IDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAuNSkNCiMjDQojIEV4dHJhY3QgY29lZmZpY2llbnRzIGZvciB0aGUgYmVzdCBsYW1iZGENCmJlc3QubGFzc28ubGFtYmRhIDwtIGN2X2xhc3NvJGxhbWJkYS5taW4NCmJlc3QucmlkZ2UubGFtYmRhIDwtIGN2X3JpZGdlJGxhbWJkYS5taW4NCmJlc3QuZWxhc3RpYy5uZXQubGFtYmRhIDwtIGN2X2VsYXN0aWNfbmV0JGxhbWJkYS5taW4NCiMjDQojIExhc3NvIFJlZ3Jlc3Npb24gKEwxIFJlZ3VsYXJpemF0aW9uKTogDQojIENBVVRJT046IG1vZGVsIGZvcm11bGEgZGlmZmVycyBmcm9tIHRoZSByZWd1bGFyIHJlZ3Jlc3Npb24gZm9ybXVsYSANCmxhc3NvX21vZGVsLm9wdCA8LSBnbG1uZXQoWF90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgeV90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLCAgICAgICMgbGFzc28gcmVncmVzc2lvbiANCiAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBiZXN0Lmxhc3NvLmxhbWJkYSkgICAjIHVzZXNlciBzZWxlY3RlZCBhbHBoYSwgb3B0aW1hbCBsYW1iZGENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjYW4gYmUgb2J0YWluZWQgdGhyb3VnaCBDViAoc2VlIGJlbG93KQ0KbGFzc29fcHJlZGljdGlvbnMub3B0IDwtIHByZWRpY3QobGFzc29fbW9kZWwub3B0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcyA9IGJlc3QubGFzc28ubGFtYmRhLCAjIHVzZXIgc2VsZWN0ZWQgbGFtYmRhIHZhbHVlIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIChyZWd1bGFyaXphdGlvbiBwYXJlbWV0ZXIpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld3ggPSBYX3Rlc3QpICAjIHRlc3QgZGF0YSBzZXQgDQojIFRoZSBmb2xsb3dpbmcgUk1TRSBvZiBwcmVkaWN0aW9uIHNlcnZlcyBhcyBhIHZhbGlkYXRpb24gLSBvbmUgc3RlcCB2YWxpZGF0aW9uDQpsYXNzb19ybXNlLm9wdCA8LSBzcXJ0KG1lYW4oKHlfdGVzdCAtIGxhc3NvX3ByZWRpY3Rpb25zLm9wdCleMikpICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiMgUmlkZ2UgUmVncmVzc2lvbiAoTDIgUmVndWxhcml6YXRpb24pDQpyaWRnZV9tb2RlbC5vcHQgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCwgbGFtYmRhID0gYmVzdC5yaWRnZS5sYW1iZGEpDQpyaWRnZV9wcmVkaWN0aW9ucy5vcHQgPC0gcHJlZGljdChyaWRnZV9tb2RlbC5vcHQsIHMgPSBiZXN0LnJpZGdlLmxhbWJkYSwgbmV3eCA9IFhfdGVzdCkNCnJpZGdlX3Jtc2Uub3B0IDwtIHNxcnQobWVhbigoeV90ZXN0IC0gcmlkZ2VfcHJlZGljdGlvbnMub3B0KV4yKSkNCg0KIyBFbGFzdGljIE5ldCAoQ29tYmluYXRpb24gb2YgTDEgYW5kIEwyKQ0KZWxhc3RpY19uZXRfbW9kZWwub3B0IDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAuNSwgbGFtYmRhID0gMC4xKQ0KZWxhc3RpY19uZXRfcHJlZGljdGlvbnMub3B0IDwtIHByZWRpY3QoZWxhc3RpY19uZXRfbW9kZWwub3B0LCBzID0gMC4xLCBuZXd4ID0gWF90ZXN0KQ0KZWxhc3RpY19uZXRfcm1zZS5vcHQgPC0gc3FydChtZWFuKCh5X3Rlc3QgLSBlbGFzdGljX25ldF9wcmVkaWN0aW9ucy5vcHQpXjIpKQ0KDQpSTVNFLm9wdCA9IGNiaW5kKExBU1NPLm9wdCA9IGxhc3NvX3Jtc2Uub3B0LCANCiAgICAgICAgICAgICAgICAgUmlkZ2Uub3B0ID0gIHJpZGdlX3Jtc2Uub3B0LCANCiAgICAgICAgICAgICAgICAgRWxhc3RpY25ldC5vcHQgPSBlbGFzdGljX25ldF9ybXNlLm9wdCkNCnBhbmRlcihSTVNFLm9wdCkNCg0KYGBgDQoNCg0KIyMgRXh0cmFjdGluZyBGaW5hbCBNb2RlbA0KDQpSZWNhbGwgdGhhdCB0aGUgb2JqZWN0aXZlIG9mIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gaXMgdG8gdXNlIHRoZSByZWd1bGFyaXphdGlvbiB0ZWNobmlxdWVzIHRvIGVzdGltYXRlIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4gVGhlIG91dGNvbWUgaXMgdGhlIGV4cGxpY2l0IHJlZ3Jlc3Npb24gZXF1YXRpb24gZm9yIHByYWN0aWNhbCBhcHBsaWNhdGlvbi4gVGhpcyBzdWJzZWN0aW9uIGV4dHJhY3RzIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhbmQgd3JpdGUgdGhlIHJlZ3Jlc3Npb24gZnVuY3Rpb24gZXhwbGljaXRseS4NCg0KPGZvbnQgY29sb3IgPSAiYmx1ZSI+Tm90ZSB0aGF0IHRoZSBmaW5hbCBtb2RlbCBpbiByZWd1bGFyaXplZCByZWdyZXNzaW9uIGlzIGRlcGVuZGVudCBvbiB0aGUgY2hvaWNlIG9mIHRoZSByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXIgJFxsYW1iZGEkLiBUaGUgZm9sbG93aW5nIGZpbmFsIG1vZGVscyBhcmUgYmFzZWQgb24gdGhlIHZhbHVlIG9mICRcbGFtYmRhJCB0aGF0IGdpdmVzIHRoZSBtaW5pbXVtIGNyb3NzLXZhbGlkYXRlZCBlcnJvciwgYGxhbWJkYS5taW5gIHRydW5kZWQgZnJvbSBgY3YuZ2xtbmV0KClgLjwvZm9udD4NCg0KDQoqKkxBU1NPIFJlZ3Jlc3Npb24gRXF1YXRpb24qKg0KDQpUaGUgcmVzdWx0aW5nIExBU1NPIHJlZ3Jlc3Npb24gZXF1YXRpb24gaXMgZ2l2ZW4gYnkNCg0KDQpgYGB7cn0NCiMjbGFzc28NCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgZm9yIHRoZSBiZXN0IGxhbWJkYQ0KYmVzdF9sYW1iZGEubGFzc28gPC0gY3ZfbGFzc28kbGFtYmRhLm1pbg0KY29lZmZpY2llbnRzLmxhc3NvIDwtIGNvZWYoY3ZfbGFzc28sIHMgPSBiZXN0X2xhbWJkYS5sYXNzbykNCiMgUmVjb25zdHJ1Y3QgdGhlIG1vZGVsIGVxdWF0aW9uDQppbnRlcmNlcHQubGFzc28gPC0gY29lZmZpY2llbnRzLmxhc3NvWzFdDQpiZXRhcy5sYXNzbyA8LSBjb2VmZmljaWVudHMubGFzc29bLTFdDQojY2F0KCJNb2RlbCBlcXVhdGlvbjogeSA9Iiwgcm91bmQoaW50ZXJjZXB0Lmxhc3NvLDQpLCAiKyIsIA0KI3Bhc3RlKHJvdW5kKGJldGFzLmxhc3NvLDQpLCBjb2xuYW1lcyhYKSwgc2VwID0gIioiLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikNCmBgYA0KDQokJA0KXHRleHR7IFByaWNlfSA9IDIyLjMzODEgIC0wLjk2NDJcdGltZXMgXHRleHR7Y3JpbX0gKyAxLjA2NTRcdGltZXMgXHRleHR7em59ICAtMC4yNzYzXHRpbWVzIFx0ZXh0e2luZHVzfSArIDAuNjUyOFx0aW1lcyBcdGV4dHtjaGFzfSAtMS43Njg1XHRpbWVzIFx0ZXh0e25veH0gDQokJA0KJCQNCisgMi4zNjc5XHRpbWVzIFx0ZXh0e3JtfSAtMC4wMDcyXHRpbWVzIFx0ZXh0e2FnZX0gLTIuOTc5NFx0aW1lcyBcdGV4dHtkaXN9ICsgMi4zNjkyXHRpbWVzIFx0ZXh0e3JhZH0gLTEuNzQwOVx0aW1lcyBcdGV4dHt0YXh9IA0KJCQNCiQkDQotMS45MjQ2XHRpbWVzIFx0ZXh0e3B0cmF0aW99ICsgMC45NDQ3XHRpbWVzIFx0ZXh0e2JsYWNrfSAtMy41ODU4XHRpbWVzIFx0ZXh0e2xzdGF0fSANCiQkDQoNCg0KDQoqKlJpZGdlIFJlZ3Jlc3Npb24gRXF1YXRpb24qKg0KDQoNClRoZSByZXN1bHRpbmcgUmlkZ2UgcmVncmVzc2lvbiBlcXVhdGlvbiBpcyBnaXZlbiBieQ0KDQpgYGB7cn0NCiMjcmlkZ2UNCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgZm9yIHRoZSBiZXN0IGxhbWJkYQ0KYmVzdF9sYW1iZGEucmlkZ2UgPC0gY3ZfcmlkZ2UkbGFtYmRhLm1pbg0KY29lZmZpY2llbnRzLnJpZGdlIDwtIGNvZWYoY3ZfcmlkZ2UsIHMgPSBiZXN0X2xhbWJkYS5yaWRnZSkNCiMgUmVjb25zdHJ1Y3QgdGhlIG1vZGVsIGVxdWF0aW9uDQppbnRlcmNlcHQucmlkZ2UgPC0gY29lZmZpY2llbnRzLnJpZGdlWzFdDQpiZXRhcy5yaWRnZSA8LSBjb2VmZmljaWVudHMucmlkZ2VbLTFdDQojY2F0KCJNb2RlbCBlcXVhdGlvbjogeSA9Iiwgcm91bmQoaW50ZXJjZXB0LnJpZGdlLDQpLCAiKyIsIA0KI3Bhc3RlKHJvdW5kKGJldGFzLnJpZGdlLDQpLCBjb2xuYW1lcyhYKSwgc2VwID0gIioiLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikNCmBgYA0KDQoNCiQkDQpcdGV4dHtwcmljZX0gPSAyMi4zMzgxICAtMC44NDAyXHRpbWVzIFx0ZXh0e2NyaW19ICsgMC44MTU3XHRpbWVzIFx0ZXh0e3pufSAgLTAuNTQyNlx0aW1lcyBcdGV4dHtpbmR1c30gKyAwLjcxOTdcdGltZXMgXHRleHR7Y2hhc30gLTEuMjQ3XHRpbWVzIFx0ZXh0e25veH0gJCQNCiQkDQorIDIuNTE4M1x0aW1lcyBcdGV4dHtybX0gLTAuMTM4N1x0aW1lcyBcdGV4dHthZ2V9IC0yLjM3MjdcdGltZXMgXHRleHR7ZGlzfSArIDEuMzg2Mlx0aW1lcyBcdGV4dHtyYWR9IC0wLjk1MTdcdGltZXMgXHRleHR7dGF4fSANCiQkDQokJA0KLTEuNzQzOVx0aW1lcyBcdGV4dHtwdHJhdGlvfSArIDAuOTM5MVx0aW1lcyBcdGV4dHtibGFja30gLTMuMjEzOFx0aW1lcyBcdGV4dHtsc3RhdH0NCiQkDQoNCg0KDQoqKkVsYXN0aWNOZXQgUmVncmVzc2lvbiBFcXVhdGlvbioqDQoNCg0KVGhlIHJlc3VsdGluZyBFbGFzdGljTmV0IHJlZ3Jlc3Npb24gZXF1YXRpb24gaXMgZ2l2ZW4gYnkNCg0KYGBge3J9DQojI3JpZGdlDQojIEV4dHJhY3QgY29lZmZpY2llbnRzIGZvciB0aGUgYmVzdCBsYW1iZGENCmJlc3RfbGFtYmRhLm5ldCA8LSBjdl9lbGFzdGljX25ldCRsYW1iZGEubWluDQpjb2VmZmljaWVudHMubmV0IDwtIGNvZWYoY3ZfZWxhc3RpY19uZXQsIHMgPSBiZXN0X2xhbWJkYS5uZXQpDQojIFJlY29uc3RydWN0IHRoZSBtb2RlbCBlcXVhdGlvbg0KaW50ZXJjZXB0Lm5ldCA8LSBjb2VmZmljaWVudHMubmV0WzFdDQpiZXRhcy5uZXQ8LSBjb2VmZmljaWVudHMubmV0Wy0xXQ0KI2NhdCgiTW9kZWwgZXF1YXRpb246IHkgPSIsIHJvdW5kKGludGVyY2VwdC5uZXQsNCksICIrIiwgDQojIHBhc3RlKHJvdW5kKGJldGFzLm5ldCw0KSwgY29sbmFtZXMoWCksIHNlcCA9ICIqIiwgY29sbGFwc2UgPSAiICsgIiksICJcbiIpDQpgYGANCg0KJCQNClx0ZXh0e3ByaWNlfSA9IDIyLjMzODEgLTEuMDAyN1x0aW1lcyBcdGV4dHtjcmltfSArIDEuMTA2OVx0aW1lcyBcdGV4dHt6bn0gLTAuMjYzMlx0aW1lcyBcdGV4dHtpbmR1c30gKyAwLjY1ODVcdGltZXMgXHRleHR7Y2hhc30gLTEuODE2Nlx0aW1lcyBcdGV4dHtub3h9IA0KJCQNCiQkDQorIDIuMzU1Mlx0aW1lcyBcdGV4dHtybX0gIC0wLjAzNjlcdGltZXMgXHRleHR7YWdlfSAgLTMuMDY4M1x0aW1lcyBcdGV4dHtkaXN9ICsgMi41NTA3XHRpbWVzIFx0ZXh0e3JhZH0gIC0xLjg5MTVcdGltZXMgXHRleHR7dGF4fSANCiQkDQokJA0KLTEuOTQxNFx0aW1lcyBcdGV4dHtwdHJhdGlvfSArIDAuOTU2Mlx0aW1lcyBcdGV4dHtibGFja30gIC0zLjU3MzNcdGltZXMgXHRleHR7bHN0YXR9IA0KJCQNCg0KDQojIyBJbnRlcmFjdGlvbiBhbmQgUG9seW1vaWFsIA0KDQpTaW5jZSBgZ2xtbmV0YCBkZWZpbmVzIHRoZSBtb2RlbCB1c2luZyB0d28gY29tcG9uZW50czogZGVzaWduIG1hdHJpeCBhbmQgcmVzcG9uc2UgbWF0cml4ICh2ZWN0b3IpLCB0aGlzIG1lYW5zIHRoYXQgd2UgaGF2ZSB0byBkZWZpbmUgaW50ZXJhY3Rpb24gdGVybXMgYW5kIGhpZ2ggb3JkZXIgcG9seW5vbWlhbHMgb2YgZmVhdHVyZSB2YXJpYWJsZXMgZGlyZWN0bHkgaW4gdGhlIGRlc2lnbiBtYXRyaXguIFRoZSBmb2xsb3dpbmcgZXhhbXBsZSBkZW1vbnN0cmF0ZXMgaG93IHRvIGluY2x1ZGUgaW50ZXJhY3Rpb24gdGVybSBpbiByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIGEgYnVpbHQtaW4gZGF0YSBzZXQgYG10Y2Fyc2AuDQoNCmBgYHtyfQ0KZGF0YShtdGNhcnMpDQoNCiMgUHJlcGFyZSBkYXRhDQpYIDwtIGFzLm1hdHJpeChtdGNhcnNbLCBjKCJocCIsICJ3dCIpXSkgICMgUHJlZGljdG9ycw0KeSA8LSBtdGNhcnMkbXBnICAjIFJlc3BvbnNlDQoNCiMgQ3JlYXRlIGludGVyYWN0aW9uIHRlcm0NCmludGVyYWN0aW9uX3Rlcm0gPC0gWFssICJocCJdICogWFssICJ3dCJdDQpYX3dpdGhfaW50ZXJhY3Rpb25zIDwtIGNiaW5kKFgsIGludGVyYWN0aW9uX3Rlcm0pDQoNCiMgRml0IGdsbW5ldCBtb2RlbA0KZml0IDwtIGdsbW5ldChYX3dpdGhfaW50ZXJhY3Rpb25zLCB5LCBhbHBoYSA9IDEpICAjIHRoaXMgZml0cyBMQVNTTyByZWdyZXNzaW9uDQojIEV4dHJhY3RzIGNvZWZmaWNpZW50cyBhdCBsYW1iZGEgPSAwLjENCiMgDQojY29lZihmaXQsIHMgPSAwLjEpICAgIA0KIyBDcm9zcy12YWxpZGF0aW9uDQpjdl9maXQgPC0gY3YuZ2xtbmV0KFhfd2l0aF9pbnRlcmFjdGlvbnMsIHksIGFscGhhID0gMSkNCg0KIyBQbG90IHRoZSBjcm9zcy12YWxpZGF0aW9uIHJlc3VsdHMgZm9yIG1hbnVhbGx5IHNlbGVjdCB0aGUgdmFsdWUgb2YgbGFtYmRhDQojcGxvdChjdl9maXQpDQoNCiMgQmVzdCBsYW1iZGEgdmFsdWU6IGF1dG9tYXRpY2FsbHkgc2VsZWN0ZWQgb3B0aW1hbCBsYW1iZGENCmJlc3RfbGFtYmRhIDwtIGN2X2ZpdCRsYW1iZGEubWluDQojcHJpbnQoYmVzdF9sYW1iZGEpDQoNCiMgUmVmaXQgdGhlIG1vZGVsIHdpdGggdGhlIGJlc3QgbGFtYmRhDQpmaW5hbF9tb2RlbCA8LSBnbG1uZXQoWF93aXRoX2ludGVyYWN0aW9ucywgeSwgYWxwaGEgPSAxLCBsYW1iZGEgPSBiZXN0X2xhbWJkYSkNCmNvZWYoZmluYWxfbW9kZWwsIHMgPSBiZXN0X2xhbWJkYSkNCmBgYA0KDQoNClwNCg0KIyBSZWd1bGFyaXplZCBMb2dpc3RpYyBSZWdyZXNzaW9uDQoNCkltcGxlbWVudGluZyByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uIChMQVNTTywgUmlkZ2UsIGFuZCBFbGFzdGljIE5ldCkgdXNpbmcgdGhlIGBnbG1uZXRgIHBhY2thZ2UgaW4gUiBpbnZvbHZlcyBzZXZlcmFsIHN0ZXBzLg0KDQoqIFNldHRpbmcgc2VlZCB0byBndWFyYW50ZWUgcmVwcm9kdWNpYmlsaXR5DQoqIFJhbmRvbSBEYXRhIFNwbGl0dGluZyBmb3IgdHJhaW5pbmcsIGNyb3NzLXZhbGlkYXRpb24sIGFuZCB0ZXN0aW5nDQoqIFN0YW5kYXJkaXppbmcgZmVhdHVyZSB2YXJpYWJsZXMgPGZvbnQgY29sb3IgPSAicmVkIj4tIHRoaXMgaXMgZXh0cmVtZWx5IGltcG9ydGFudCBpbiByZWd1bGFyaXphdGlvbiE8L2ZvbnQ+DQoqIGJ1aWxkaW5nIHJlZ3VsYXJpemVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIGluY2x1ZGluZyBjcm9zcy12YWxpZGF0aW9uIGZvciAkXGxhbWJkYSQNCiogUHJlZGljdGluZyB0aGUgcmVzcG9uc2Ugd2l0aCB0aGUgdGVzdGluZyBkYXRhIHNldCB1c2luZyB0aGUgZGVmYXVsdCBjdXQtb2ZmIHByb2JhYmlsaXR5DQoqIEV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBtZWFzdXJlOiBjb25mdXNpb24gbWF0cml4DQoqIENvbXBhcmluZyBjYW5kaWF0ZSBtb2RlbHMgYW5kIHZpc3VhbGl6aW5nIHJlc3VsdHMNCiogUmVwb3J0aW5nIHRoZSBmaW5hbCByZXN1bHRpbmcgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBtb2RlbHMgKHdpdGggZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzKQ0KDQoNCk5leHQsIHdlIHVzIHRoZSBwb3B1bGFyICoqUGltYSBJbmRpYW4gRGlhYmV0ZXMqKiBkYXRhIHNldCBpbiBSIGxpYnJhcnkgYG1sYmVuY2hgIHRvIGRlbW9uc3RyYXRlIHRoZSBpbXBsZW1lbnRhdGlvbiBvZiByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uLiBUaGUgZGF0YXNldCBpcyBvcmlnaW5hbGx5IGZyb20gdGhlIE5hdGlvbmFsIEluc3RpdHV0ZSBvZiBEaWFiZXRlcyBhbmQgRGlnZXN0aXZlIGFuZCBLaWRuZXkgRGlzZWFzZXMuIFRoZSBvYmplY3RpdmUgb2YgdGhlIGRhdGFzZXQgaXMgdG8gZGlhZ25vc3RpY2FsbHkgcHJlZGljdCB3aGV0aGVyIG9yIG5vdCBhIHBhdGllbnQgaGFzIGRpYWJldGVzLCBiYXNlZCBvbiBjZXJ0YWluIGRpYWdub3N0aWMgbWVhc3VyZW1lbnRzIGluY2x1ZGVkIGluIHRoZSBkYXRhc2V0LiBTZXZlcmFsIGNvbnN0cmFpbnRzIHdlcmUgcGxhY2VkIG9uIHRoZSBzZWxlY3Rpb24gb2YgdGhlc2UgaW5zdGFuY2VzIGZyb20gYSBsYXJnZXIgZGF0YWJhc2UuIEluIHBhcnRpY3VsYXIsIGFsbCBwYXRpZW50cyBoZXJlIGFyZSBmZW1hbGVzIGF0IGxlYXN0IDIxIHllYXJzIG9sZCBvZiBQaW1hIEluZGlhbiBoZXJpdGFnZS4NCg0KVGhlIGRhdGEgc2V0cyBjb25zaXN0cyBvZiBzZXZlcmFsIG1lZGljYWwgcHJlZGljdG9yIHZhcmlhYmxlcyBhbmQgb25lIHRhcmdldCB2YXJpYWJsZSwgT3V0Y29tZSB3aXRoIDc2OCBvYnNlcnZhdGlvbnMuDQoNCmBwcmVnbmFudGAgTnVtYmVyIG9mIHRpbWVzIHByZWduYW50Lg0KDQpgZ2x1Y29zZWAgUGxhc21hIGdsdWNvc2UgY29uY2VudHJhdGlvbiAoZ2x1Y29zZSB0b2xlcmFuY2UgdGVzdCkuDQoNCmBwcmVzc3VyZWAgRGlhc3RvbGljIGJsb29kIHByZXNzdXJlIChtbSBIZykuDQoNCmB0cmljZXBzYCBUcmljZXBzIHNraW4gZm9sZCB0aGlja25lc3MgKG1tKS4NCg0KYGluc3VsaW5gIDItSG91ciBzZXJ1bSBpbnN1bGluIChtdSBVL21sKS4NCg0KYG1hc3NgIEJvZHkgbWFzcyBpbmRleCAod2VpZ2h0IGluIGtnLyhoZWlnaHQgaW4gbSleMikuDQoNCmBwZWRpZ3JlZWAgRGlhYmV0ZXMgcGVkaWdyZWUgZnVuY3Rpb24uDQoNCmBhZ2VgIEFnZSAoeWVhcnMpLg0KDQpgZGlhYmV0ZXNgIEZhY3RvciBpbmRpY2F0aW5nIHRoZSBkaWFiZXRlcyB0ZXN0IHJlc3VsdCAobmVnL3BvcyA9IDAvMSkuDQoNClRoZSBkYXRhIHNldCBpcyBhbHNvIGF2YWlsYWJsZSBhdCA8aHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUyL3cwNy9kaWFiZXRlcy5jc3Y+DQoNCg0KVGhlIG9iamVjdGl2ZSBvZiBwZXJmb3JtaW5nIGxvZ2lzdGljIG1vZGVsaW5nIGlzIHRvIHByZWRpY3QgZGlhYmV0ZXMgYmFzZWQgb24gdmFyaWFibGUgcmlzayBmYWN0b3JzLiBXZSB3aWxsIGZvbGxvdyB0aGUgYWJvdmUgc3RlcHMgdG8gcGVyZm9ybSB0aHJlZSByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscy4gDQoNCg0KIyMgQ29lZmZpY2llbnQgUGF0aCBBbmFseXNpcw0KDQpJbiByZWd1bGFyaXplZCByZWdyZXNzaW9uLCB0aGUgY29lZmZpY2llbnQgcGF0aCBhbmQgbWVhc3VyZXMgb2YgZml0IGFyZSBlc3NlbnRpYWwgdG9vbHMgZm9yIHVuZGVyc3RhbmRpbmcgbW9kZWwgcGVyZm9ybWFuY2UgYW5kIHNlbGVjdGluZyB0aGUgb3B0aW1hbCByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXIuIA0KDQoNCg0KYGBge3J9DQojIGxvYWRpbmcgcmVsYXRlZCBwYWNrYWdlcw0KIyBsaWJyYXJ5KG1sYmVuY2gpDQojIGxpYnJhcnkoZ2xtbmV0KQ0KIyBsaWJyYXJ5KGNhcmV0KQ0KZGF0YSgiUGltYUluZGlhbnNEaWFiZXRlcyIpDQpkZiA8LSBQaW1hSW5kaWFuc0RpYWJldGVzDQoNCiMgQ29udmVydCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgdG8gYSBiaW5hcnkgbnVtZXJpYyB2YXJpYWJsZQ0KZGYkZGlhYmV0ZXMgPC0gaWZlbHNlKGRmJGRpYWJldGVzID09ICJwb3MiLCAxLCAwKQ0KDQojIFNwbGl0IHRoZSBkYXRhIGludG8gcHJlZGljdG9ycyAoWCkgYW5kIHJlc3BvbnNlICh5KQ0KWCA8LSBtb2RlbC5tYXRyaXgoZGlhYmV0ZXMgfiAuLCBkZilbLC0xXSAgIyBSZW1vdmUgdGhlIGludGVyY2VwdCBjb2x1bW4NCnkgPC0gZGYkZGlhYmV0ZXMNCg0KIyBTcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMNCnNldC5zZWVkKDEyMykNCnRyYWluSW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5LCBwID0gMC44LCBsaXN0ID0gRkFMU0UpDQpYX3RyYWluIDwtIFhbdHJhaW5JbmRleCwgXQ0KWF90ZXN0IDwtIFhbLXRyYWluSW5kZXgsIF0NCnlfdHJhaW4gPC0geVt0cmFpbkluZGV4XQ0KeV90ZXN0IDwtIHlbLXRyYWluSW5kZXhdDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIEZpdCBMQVNTTyBtb2RlbA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMNCmxhc3NvX21vZGVsIDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBmYW1pbHkgPSAiYmlub21pYWwiLCBhbHBoYSA9IDEpDQoNCiMgQ3Jvc3MtdmFsaWRhdGlvbiB0byBmaW5kIHRoZSBvcHRpbWFsIGxhbWJkYQ0KY3ZfbGFzc28gPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMSkNCg0KIyBPcHRpbWFsIGxhbWJkYQ0KbGFtYmRhX2xhc3NvIDwtIGN2X2xhc3NvJGxhbWJkYS5taW4NCg0KIyBSZWZpdCB0aGUgbW9kZWwgd2l0aCB0aGUgb3B0aW1hbCBsYW1iZGENCmxhc3NvX21vZGVsX29wdCA8LSBnbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBsYW1iZGFfbGFzc28pDQpgYGANCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD00fQ0KIyMgVmlzdWFsaXplIHRoZSBpbXBhY3Qgb2YgbGFtYmRhIG9uIHNocmlua2luZyBjb2VmZmljaWVudHMNCiMgUGxvdCBjb2VmZmljaWVudCBwYXRocw0KcGFyKG1hcj1jKDUsNCw2LDIpLCBtZnJvdz1jKDEsMikpICMgDQpwbG90KGxhc3NvX21vZGVsLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsID0gVFJVRSwNCiAgICAgY29sID0gcmFpbmJvdyg4KSwNCiAgICAgbHdkID0gMSwNCiAgICAgbWFpbiA9ICJDb2VmZmljaWVudCBQYXRoIFBsb3Q6IExBU1NPIiwNCiAgICAgY2V4Lm1haW4gPSAwLjgpDQp0ZXh0KC02LCAwLjQsICJtaW5pbXVtIENWIGVycm9yIiwgY29sPSJyZWQiLCBjZXggPSAwLjYgKQ0KYWJsaW5lKHYgPSBsb2coY3ZfZml0JGxhbWJkYS5taW4pLCBjb2wgPSAicmVkIiwgbHR5ID0gNCwgbHdkID0gMSkNCmFibGluZSh2ID0gbG9nKGN2X2ZpdCRsYW1iZGEuMXNlKSwgY29sID0gImJsdWUiLCBsdHkgPSA0LCBsd2QgPSAxKQ0KDQpwbG90KGN2X2xhc3NvLCBtYWluPSJNZWFzdXJlIG9mIE1vZGVsIEZpdDogTEFTU08iLCBjZXgubWFpbiA9IDAuOCkNCg0KYGBgDQoNCg0KRmlyc3QsIHNpbmNlICRcbGFtYmRhJCBpbiByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVscyBpcyB0eXBpY2FsbHkgYSBzbWFsbCBwb3NpdGl2ZSBudW1iZXIsIGEgbG9nYXJpdGhtaWMgc2NhbGUgd2FzIHVzZWQgZm9yIHRoZSBjb2VmZmljaWVudCBwYXRoIGFuZCBwZXJmb3JtYW5jZSBmaXQgcGxvdHMuIEFzICRcbGFtYmRhJCBpbmNyZWFzZXMsIHdlIG9ic2VydmUgdGhhdCB0aGUgbWFnbml0dWRlIG9mIHRoZSBjb2VmZmljaWVudHMgZGVjcmVhc2VzIChsZWZ0IHBhbmVsKSwgd2hpbGUgdGhlIGRldmlhbmNlIChlcnJvcikgaW5jcmVhc2VzIChyaWdodCBwYW5lbCkuIFRoZSB0d28gdmVydGljYWwgcmVmZXJlbmNlIGxpbmVzIHNlcnZlIGFzIGd1aWRlcyBmb3Igc2VsZWN0aW5nIGFuIGFwcHJvcHJpYXRlIHZhbHVlIG9mICRcbGFtYmRhJCBpbiBwcmFjdGljYWwgYXBwbGljYXRpb25zLg0KDQoNCiogQSBsYXJnZXIgdmFsdWUgb2YgJFxsYW1iZGEkIGNhbiBsZWFkIHRvIHBvdGVudGlhbCB1bmRlcmZpdHRpbmcsIGFzIG1vcmUgY29lZmZpY2llbnRzIGFyZSBzaHJ1bmsgdG93YXJkIHplcm8gKGxlZnQgcGFuZWwpLCByZXN1bHRpbmcgaW4gYSBsYXJnZXIgZXJyb3IgKHJpZ2h0IHBhbmVsKS4NCg0KKiBBIHNtYWxsZXIgdmFsdWUgb2YgJFxsYW1iZGEkIGNhbiBsZWFkIHRvIHBvdGVudGlhbCBvdmVyZml0dGluZywgYXMgZmV3ZXIgY29lZmZpY2llbnRzIGFyZSBzaHJ1bmssIGFsbG93aW5nIG1vcmUgdG8gcmVtYWluIGluIHRoZSBtb2RlbCwgd2hpY2ggcmVzdWx0cyBpbiBhIHNtYWxsZXIgZXJyb3IuDQoNCg0KDQojIyBSZWd1bGFyaXphdGlvbiBQYXJhbWV0ZXIgRGV0ZXJtaW5hdGlvbg0KDQpUbyBhdm9pZCBwb3RlbnRpYWwgaXNzdWVzIG9mIG92ZXJmaXR0aW5nIGFuZCB1bmRlcmZpdHRpbmcsIGEgY3Jvc3MtdmFsaWRhdGlvbiB3YXMgdXNlZCBpbiBgZ2xtbmV0YCB0byBwcm92aWRlIGEgcmFuZ2Ugb2Ygc3VnZ2VzdGVkIHZhbHVlIGZvciB0aGUgcmVndWxhcml6YXRpb24gcGFyYW1ldGVyICRcbGFtYmRhJDogdGhlIG1pbmltdW0gJFxsYW1iZGEkIHRvIGF2b2lkIG92ZXJmaXR0aW5nIGFuZCBiaWdnZXN0ICRcbGFtYmRhJCB0byBhdm9pZCB1bmRlcmZpdHRpbmcgb2YgdGhlIG1vZGVsLg0KDQoNCmBgYHtyfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgRml0IFJpZGdlIG1vZGVsDQojIyMjIyMjIyMjIyMjIyMjIyMjIw0KcmlkZ2VfbW9kZWwgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMCkNCg0KIyBDcm9zcy12YWxpZGF0aW9uIHRvIGZpbmQgdGhlIG9wdGltYWwgbGFtYmRhDQpjdl9yaWRnZSA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAwKQ0KDQojIE9wdGltYWwgbGFtYmRhDQpsYW1iZGFfcmlkZ2UgPC0gY3ZfcmlkZ2UkbGFtYmRhLm1pbg0KDQojIFJlZml0IHRoZSBtb2RlbCB3aXRoIHRoZSBvcHRpbWFsIGxhbWJkYQ0KcmlkZ2VfbW9kZWxfb3B0IDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYSA9IGxhbWJkYV9yaWRnZSkNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIEZpdCBFbGFzdGljIE5ldCBtb2RlbCAoZS5nLiwgYWxwaGEgPSAwLjUpDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KZWxhc3RpY19tb2RlbCA8LSBnbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAwLjUpDQoNCiMgQ3Jvc3MtdmFsaWRhdGlvbiB0byBmaW5kIHRoZSBvcHRpbWFsIGxhbWJkYQ0KY3ZfZWxhc3RpYyA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAwLjUpDQoNCiMgT3B0aW1hbCBsYW1iZGENCmxhbWJkYV9lbGFzdGljIDwtIGN2X2VsYXN0aWMkbGFtYmRhLm1pbg0KDQojIFJlZml0IHRoZSBtb2RlbCB3aXRoIHRoZSBvcHRpbWFsIGxhbWJkYQ0KZWxhc3RpY19tb2RlbF9vcHQgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBsYW1iZGFfZWxhc3RpYykNCmBgYA0KDQpgYGB7cn0NCmxhc3NvLmNvZWYgPC0gYXMubWF0cml4KGNvZWYobGFzc29fbW9kZWxfb3B0KSkNCnJpZGdlLmNvZWYgPC0gYXMubWF0cml4KGNvZWYocmlkZ2VfbW9kZWxfb3B0KSkNCmVsYXN0aWMuY29lZiA8LSBhcy5tYXRyaXgoY29lZihlbGFzdGljX21vZGVsX29wdCkpDQpyZWd1bGFyaXplZC5jb2VmIDwtIGRhdGEuZnJhbWUobGFzc28gPSBsYXNzby5jb2VmWywxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWRnZSA9IHJpZGdlLmNvZWZbLDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBlbGFzdGljbmV0ID0gZWxhc3RpYy5jb2VmWywxXSkNCnBhbmRlcihyZWd1bGFyaXplZC5jb2VmKQ0KYGBgDQoNCg0KIyMgT3B0aW1hbCBDdXQwZmYgUHJvYmFiaWxpdHkgRGV0ZXJtaW5hdGlvbg0KDQoNCioqT3B0aW1hbCBDdXQtb2ZmIFByb2JhYmlsaXR5IERldGVybWluYXRpb24qKg0KDQpJbiBvcmRlciB1c2UgdGhlIGZpdHRlZCBtb2RlbCBmb3IgcHJlZGljdGluZyB0aGUgbGFiZWwgb3IgY2xhc3NpZmljYXRpb24sIHdlIG5lZWQgdG8gaWRlbnRpZnkgdGhlIG9wdGltYWwgY3V0LW9mZiBwcm9iYWJpbGl0eSBpbnN0ZWFkIG9mIHVzaW5nIHRoZSBkZWZhdWx0IGN1dC1vZmYgcHJvYmFiaWxpdHkgb2YgMC41Lg0KDQoNCmBgYHtyfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgUHJlZGljdCBvbiB0aGUgdGVzdCBzZXQ6IHR5cGUgPSAiY2xhc3MiIHVzZXMgdGhlIGRlZmF1bHQgDQojIGN1dC1vZmYgcHJvYmFiaWxpdHkgdG8gYmUgMC41Lg0KcHJlZGljdF9sYXNzbyA8LSBwcmVkaWN0KGxhc3NvX21vZGVsX29wdCwgbmV3eCA9IFhfdGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpDQpwcmVkaWN0X3JpZGdlIDwtIHByZWRpY3QocmlkZ2VfbW9kZWxfb3B0LCBuZXd4ID0gWF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCnByZWRpY3RfZWxhc3RpYyA8LSBwcmVkaWN0KGVsYXN0aWNfbW9kZWxfb3B0LCBuZXd4ID0gWF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgT3B0aW1hbCBjdXRvZmYgcHJvYmFiaWxpdHkgZGV0ZXJtaW5hdGlvbg0Kc2VxLmN1dCA8LSBzZXEoMCwxLCBsZW5ndGg9NTApDQojIHkgaXMgYSB2ZWN0b3Igb2YgMCBhbmQgMQ0KYWNjLmxhc3NvIDwtIE5VTEwNCmFjYy5yaWRnZSA8LSBOVUxMDQphY2MuZWxhc3RpYyA8LSBOVUxMDQpmb3IgKGkgaW4gMTpsZW5ndGgoc2VxLmN1dCkpew0KICAgcHJlZHkubGFzc28gPC0gaWZlbHNlKHByZWRpY3RfbGFzc28gPnNlcS5jdXRbaV0sIDEsIDApDQogICBwcmVkeS5yaWRnZTwtIGlmZWxzZShwcmVkaWN0X3JpZGdlID5zZXEuY3V0W2ldLCAxLCAwKQ0KICAgcHJlZHkuZWxhc3RpYzwtIGlmZWxzZShwcmVkaWN0X2VsYXN0aWMgPnNlcS5jdXRbaV0sIDEsIDApDQogICAjIw0KICAgYWNjLmxhc3NvW2ldIDwtIG1lYW4oeV90ZXN0ICA9PSBwcmVkeS5sYXNzbykNCiAgIGFjYy5yaWRnZVtpXSA8LSBtZWFuKHlfdGVzdCA9PSBwcmVkeS5yaWRnZSkNCiAgIGFjYy5lbGFzdGljW2ldIDwtIG1lYW4oeV90ZXN0ICA9PSBwcmVkeS5lbGFzdGljKQ0KfQ0KIyMgb3B0aW1hbCBjdXQtb2ZmOiBpZiB0aGUgbWF4aW11bSBhY2N1cmFjeSBvY2N1cnMgYXQgbXVsdGlwbGUNCiMjIGN1dC1vZmYgcHJvYmFiaWxpdGllcywgdGhlIGF2ZXJhZ2Ugb2YgdGhlc2UgY3V0b2ZmIHByb2JhYmlsaXRpZXMNCiMjIHdpbGwgYmUgZGVmaW5lZCBhcyB0aGUgb3B0aW1hbCBjdXRvZmYgcHJvYmFiaWxpdHkNCm9wdC5jdXQubGFzc28gPC0gbWVhbihzZXEuY3V0W3doaWNoKGFjYy5sYXNzbz09bWF4KGFjYy5sYXNzbykpXSkNCm9wdC5jdXQucmlkZ2U8LSBtZWFuKHNlcS5jdXRbd2hpY2goYWNjLnJpZGdlPT1tYXgoYWNjLnJpZGdlKSldKQ0Kb3B0LmN1dC5lbGFzdGljIDwtIG1lYW4oc2VxLmN1dFt3aGljaChhY2MuZWxhc3RpYz09bWF4KGFjYy5lbGFzdGljKSldKQ0KIyMNCmFjYy5kYXRhIDwtIGRhdGEuZnJhbWUocHJvYiA9IHJlcChzZXEuY3V0LDMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgYWNjPWMoYWNjLmxhc3NvLCBhY2MucmlkZ2UsIGFjYy5lbGFzdGljKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gYyhyZXAoImxhc3NvIiw1MCksIHJlcCgicmlkZ2UiLDUwKSwgcmVwKCJlbGFzdGljIiw1MCkpKQ0KYGBgDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCiMjDQpnZy5hY2MgPC0gZ2dwbG90KGRhdGEgPSBhY2MuZGF0YSwgYWVzKHg9cHJvYiwgeSA9IGFjYywgY29sb3IgPSBncm91cCkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjYsIHkgPSAwLjQ1LCANCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiTEFTU08gY3V0b2ZmOiAiLCByb3VuZChvcHQuY3V0Lmxhc3NvLDUpLCAiQWNjdXJhY3k6ICIsIHJvdW5kKG1heChhY2MubGFzc28pLDUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAiXG5SaWRnZSBjdXRvZmY6ICIsIHJvdW5kKG9wdC5jdXQucmlkZ2UsNSksICJBY2N1cmFjeTogIiwgcm91bmQobWF4KGFjYy5yaWRnZSksNSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJcbkVsYXN0aWMgY3V0b2ZmOiAiLCByb3VuZChvcHQuY3V0LmVsYXN0aWMsNSksICJBY2N1cmFjeTogIiwgcm91bmQobWF4KGFjYy5lbGFzdGljKSw1KSksIA0KICAgICAgICAgICBzaXplID0gMywgDQogICAgICAgICAgIGNvbG9yID0gIm5hdnkiKSArDQogIGdndGl0bGUoIkN1dC1vZmYgUHJvYmFiaWxpdHkgdnMgQWNjdXJhY3kiKSArDQogIGxhYnMoeCA9ICJjdXQtb2ZmIFByb2JhYmlsaXR5IiwgDQogICAgICAgeSA9ICJhY2N1cmFjeSIsIGNvbG9yID0gIkdyb3VwIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KIyMNCmdncGxvdGx5KGdnLmFjYykNCmBgYA0KDQpUaGUgZmlndXJlIHNob3dzIHRoZSBvcHRpbWFsIGN1dG9mZiBwcm9iYWJpbGl0aWVzIG9mIHRoZSB0aHJlZSByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVscy4NCg0KDQoqKkxvY2FsIFBlcmZvcm1hbmNlIE1lYXN1cmVzKioNCg0KV2l0aCB0aGUgb3B0aW1hbCBjdXQtb2ZmIHByb2JhYmlsaXRpZXMgb2YgdGhlIGNvcnJlc3BvbmRpbmcgdGhyZWUgcmVndWxhcml6ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMsIHdlIG5vdyBjYWxjdWxhdGUgdGhlIGxvY2FsIHBlcmZvcm1hbmNlIG1lYXN1cmVzIGJhc2VkIG9uIHRoZWlyIGNvbmZ1c2lvbiBtYXRyaWNlcy4NCg0KDQp8ICAgICAgICAgICAgICAgIHxSZWYuIFBvc2l0aXZlICB8IFJlZi4gTmVnYXRpdmUgIHwNCnw6LS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tfA0KfFByZWQuIFBvc2l0aXZlICB8ICAgQSAgICAgICAgfCAgICAgQiAgICAgfA0KfFByZWQuIE5lZ2F0aXZlICB8ICAgQyAgICAgICAgfCAgICAgRCAgICAgfA0KDQoNClZhcmlvdXMgbG9jYWwgbWVhc3VyZXMgYXJlIHN1bW1hcml6ZWQgaW4gdGhlIGZvbGxvd2luZw0KDQoqIFNlbnNpdGl2aXR5ICAkXHRleHR7c2VufSA9IEEvKEEgKyBDKSQNCiogU3BlY2lmaWNpdHkgICRcdGV4dHtzcGV9ID0gRC8oQiArIEQpJA0KKiBQcmV2YWxlbmNlICRcdGV4dHtQcmV2YWxlbmNlfSA9KEEgKyBDKS8oQSArIEIgKyBDICsgRCkkDQoqIFBvc2l0aXZlIFByZWRpY3RpdmUgVmFsdWUgDQoNCiQkDQpcdGV4dHtQUFZ9ID0gXGZyYWN7XHRleHR7c2VufVx0aW1lcyBcdGV4dHtwcmV2YWxlbmNlfX17XHRleHR7c2VufVx0aW1lcyBcdGV4dHtwcmV2YWxlbmNlfSArICgxLVx0ZXh0e3Nlbn0pXHRpbWVzICgxLVx0ZXh0e3ByZXZhbGVuY2V9KX0NCiQkDQoNCiogTmVnYXRpdmUgUHJlZGljdGVkIFZhbHVlDQoNCiQkDQpcdGV4dHtQUFZ9ID0gXGZyYWN7XHRleHR7c3BlfVx0aW1lcyAoMS1cdGV4dHtwcmV2YWxlbmNlfSl9eygxLVx0ZXh0e3NwZX0pXHRpbWVzIFx0ZXh0e3ByZXZhbGVuY2V9ICsgXHRleHR7c3BlfVx0aW1lcyAoMS1cdGV4dHtwcmV2YWxlbmNlfSl9DQokJA0KDQoqIERldGVjdGlvbiBSYXRlICRcdGV4dHtEZXRlY3Rpb25SYXRlID0gQS8oQSArIEIgKyBDICsgRCl9JA0KDQoqIERldGVjdGlvbiBQcmV2YWxlbmNlICRcdGV4dHtEZXRlY3Rpb25QcmV2YWxlbmNlfT0oQSArIEIpLyAoQSArIEIgKyBDICsgRCkkDQoNCiogQmFsYW5jZWQgQWNjdXJhY3kgJFx0ZXh0e0JhbGFuY2VkQWNjdXJhY3l9ID0gKFx0ZXh0e3Nlbn0gKyBcdGV4dHtzcGV9KS8yJA0KDQoqIFByZWNpc2lvbiAkXHRleHR7UHJlY2lzaW9ufSA9IEEvKEEgKyBCKSQNCg0KKiBSZWNhbGwgJFx0ZXh0e1JlY2FsbH0gPSBBLyhBICsgQykkDQoNCiogRjEgU2NvcmUgJEYxID0gKDErXGJldGFeMilcdGltZXMgXHRleHR7UHJlY2lzaW9ufVx0aW1lcyBcdGV4dHtSZWNhbGx9KFxiZXRhXjJcdGltZXMgXHRleHR7UHJlY2lzaW9ufSArIFx0ZXh0e1JlY2FsbH0pJA0KDQpSIGZ1bmN0aW9uIGBjb25mdXNpb25NYXRyaXgoKWAgaW4gbGlicmFyeSBgY2FyZXRgIGNhbGN1bGF0ZXMgdGhlIGFib3ZlIGxvY2FsIG1lYXN1cmVzLiANCg0KDQpgYGB7cn0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgdXNpbmcgdGhlIG9wdGltYWwgY3V0b2ZmIHByb2JhYmlsaXR5IHRvIHByZWRpY3QgbGFiZWxzDQojIyANCnByZWQubGFiLmxhc3NvIDwtIGlmZWxzZShwcmVkaWN0X2xhc3NvID5vcHQuY3V0Lmxhc3NvLCAxLCAwKQ0KcHJlZC5sYWIucmlkZ2U8LSBpZmVsc2UocHJlZGljdF9yaWRnZSA+b3B0LmN1dC5yaWRnZSwgMSwgMCkNCnByZWQubGFiLmVsYXN0aWM8LSBpZmVsc2UocHJlZGljdF9lbGFzdGljID5vcHQuY3V0LmVsYXN0aWMsIDEsIDApDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIENvbnZlcnQgcHJlZGljdGlvbnMgdG8gZmFjdG9ycw0KcHJlZC5sYWIubGFzc28uZmN0IDwtIGFzLmZhY3RvcihwcmVkLmxhYi5sYXNzbykNCnByZWQubGFiLnJpZGdlLmZjdCA8LSBhcy5mYWN0b3IocHJlZC5sYWIucmlkZ2UpDQpwcmVkLmxhYi5lbGFzdGljLmZjdCA8LSBhcy5mYWN0b3IocHJlZC5sYWIuZWxhc3RpYykNCg0KIyBDb252ZXJ0IGFjdHVhbCB2YWx1ZXMgdG8gZmFjdG9ycw0KeV90ZXN0IDwtIGFzLmZhY3Rvcih5X3Rlc3QpDQoNCiMgQ29uZnVzaW9uIE1hdHJpeCBhbmQgTWV0cmljcw0KY29uZnVzaW9uLmxhc3NvIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkLmxhYi5sYXNzby5mY3QsIHlfdGVzdCkNCmNvbmZ1c2lvbi5yaWRnZTwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkLmxhYi5yaWRnZS5mY3QsIHlfdGVzdCkNCmNvbmZ1c2lvbi5lbGFzdGljIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkLmxhYi5lbGFzdGljLmZjdCwgeV90ZXN0KQ0KDQojIyBDb21tb25seSB1c2VkIHBlcmZvcm1hbmNlIG1lYXN1cmVkDQpQZXJmTWVhc3VyZXMgPC0gY2JpbmQobGFzc28gPSBjb25mdXNpb24ubGFzc28kYnlDbGFzcywgDQogICAgICAgICAgICAgICAgICAgICByaWRnZSA9IGNvbmZ1c2lvbi5yaWRnZSRieUNsYXNzLCANCiAgICAgICAgICAgICAgICAgICAgIGVsYXN0aWMgPSBjb25mdXNpb24uZWxhc3RpYyRieUNsYXNzKQ0KcGFuZGVyKFBlcmZNZWFzdXJlcykNCmBgYA0KDQoNCg0KIyMgUk9DIEFuYWx5c2lzDQoNClRvIGNvbXBhcmUgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSB0aHJlZSByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyAoTEFTU08sIFJpZGdlLCBhbmQgRWxhc3RpYyBOZXQpIHVzaW5nIFJPQyBhbmFseXNpcywgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgQXJlYSBVbmRlciB0aGUgQ3VydmUgKEFVQykgYW5kIHBsb3QgdGhlIFJPQyBjdXJ2ZXMuIFRoZSBgcFJPQ2AgcGFja2FnZSBpbiBSIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIHRoaXMgcHVycG9zZS4NCg0KDQoNCmBgYHtyfQ0KIyBsaWJyYXJ5KHBST0MpDQojIFByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGZvciBlYWNoIG1vZGVsOiB0eXBlID0gInJlc3BvbnNlIg0KcHJvYl9sYXNzbyA8LSBwcmVkaWN0KGxhc3NvX21vZGVsX29wdCwgbmV3eCA9IFhfdGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpDQpwcm9iX3JpZGdlIDwtIHByZWRpY3QocmlkZ2VfbW9kZWxfb3B0LCBuZXd4ID0gWF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCnByb2JfZWxhc3RpYyA8LSBwcmVkaWN0KGVsYXN0aWNfbW9kZWxfb3B0LCBuZXd4ID0gWF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyBDb21wdXRlIFJPQyBjdXJ2ZXM6IHJvYyBvYmplY3QgY29udGFpbnMgYSBsb3QgaW5mb3JtYXRpb24gaW5jbHVkaW5nDQojIHNlbnNpdGl2aXR5LCBzcGVjaWZpY2l0eSwgQVVDLCBldGMuDQpyb2NfbGFzc28gPC0gcm9jKHlfdGVzdCwgcHJvYl9sYXNzbykNCnJvY19yaWRnZSA8LSByb2MoeV90ZXN0LCBwcm9iX3JpZGdlKQ0Kcm9jX2VsYXN0aWMgPC0gcm9jKHlfdGVzdCwgcHJvYl9lbGFzdGljKQ0KDQojIENvbXB1dGUgQVVDIHZhbHVlcw0KYXVjX2xhc3NvIDwtIGF1Yyhyb2NfbGFzc28pDQphdWNfcmlkZ2UgPC0gYXVjKHJvY19yaWRnZSkNCmF1Y19lbGFzdGljIDwtIGF1Yyhyb2NfZWxhc3RpYykNCg0KIyMgTEFTU08NCnNlbi5sYXNzbyA8LSByb2NfbGFzc28kc2Vuc2l0aXZpdGllcw0Kc3BlLmxhc3NvIDwtIHJvY19sYXNzbyRzcGVjaWZpY2l0aWVzDQphdWMubGFzc28gPC0gcm9jX2xhc3NvJGF1Yw0KDQojIyBSaWRnZQ0Kc2VuLnJpZGdlIDwtIHJvY19yaWRnZSRzZW5zaXRpdml0aWVzDQpzcGUucmlkZ2UgPC0gcm9jX3JpZGdlJHNwZWNpZmljaXRpZXMNCmF1Yy5yaWRnZSA8LSByb2NfcmlkZ2UkYXVjDQoNCiMjIEVsYXN0aWMgTmV0DQpzZW4uZWxhc3RpYyA8LSByb2NfZWxhc3RpYyRzZW5zaXRpdml0aWVzDQpzcGUuZWxhc3RpYyA8LSByb2NfZWxhc3RpYyRzcGVjaWZpY2l0aWVzDQphdWMuZWxhc3RpYyA8LSByb2NfZWxhc3RpYyRhdWMNCg0KIyMgUGxvdHRpbmcgdGhlIFJPQyBjdXJ2ZXM6IHRocmVlIGNvbG9ycyAtIGdyZWVuLCBvcmFuZ2UsIGFuZCBwdXJwbGUNCg0KcGxvdCgxLXNwZS5sYXNzbywgc2VuLmxhc3NvLCANCiAgICAgdHlwZSA9ICJsIiwNCiAgICAgY29sID0gImdyZWVuIiwgDQogICAgIHhsaW09YygwLDEpLA0KICAgICB4bGFiID0gIjEgLSBzcGVjaWZpY2l0eSIsDQogICAgIHlsYWIgPSAic2Vuc2l0aXZpdHkiLA0KICAgICBtYWluID0gIlJPQyBDdXJ2ZXMgZm9yIExBU1NPLCBSaWRnZSwgYW5kIEVsYXN0aWMgTmV0IikNCmxpbmVzKDEtc3BlLnJpZGdlLCBzZW4ucmlkZ2UsIGNvbCA9ICJvcmFuZ2UiKQ0KbGluZXMoMS1zcGUuZWxhc3RpYywgc2VuLmVsYXN0aWMsIGNvbCA9ICJwdXJwbGUiKQ0KYWJsaW5lKDAsMSwgdHlwZSA9ICJsIiwgbHR5ID0gMiwgY29sID0gInN0ZWVsYmx1ZSIsIGx3ZCA9IDEpDQoNCiMgQWRkIGxlZ2VuZA0KbGVnZW5kKCJib3R0b21yaWdodCIsIGxlZ2VuZCA9IGMocGFzdGUoIkxBU1NPIChBVUMgPSIsIHJvdW5kKGF1Y19sYXNzbywgMyksICIpIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCJSaWRnZSAoQVVDID0iLCByb3VuZChhdWNfcmlkZ2UsIDMpLCAiKSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiRWxhc3RpYyBOZXQgKEFVQyA9Iiwgcm91bmQoYXVjX2VsYXN0aWMsIDMpLCAiKSIpKSwNCiAgICAgICBjb2wgPSBjKCJncmVlbiIsICJvcmFuZ2UiLCAicHVycGxlIiksIGx0eSA9IDEsIGNleCA9IDAuOCwgYnR5ID0gIm4iKQ0KYGBgDQoNClRoZSBhYm92ZSBST0MgY3VydmVzIGFuZCB0aGUgY29ycmVzcG9uZGluZyBBVUNzIGFyZSBzaW1pbGFyIHRvIGVhY2ggb3RoZXIuIFRoYXQgaXMsIHRoZSB0aHJlZSByZWd1bGFyaXplZCByZWdyZXNzaW9uIHBlcmZvcm1lZCBlcXVhbGx5IHdlbGwuDQoNCg0KIyBNb2RlbCBTZWxlY3Rpb24gb2YgUmVndWxhcml6ZWQgUmVncmVzc2lvbg0KDQpJZiB0aGUgY2FuZGlkYXRlIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbW9kZWxzIGFyZSBlcXVhbGx5IHdlbGwtcGVyZm9ybWVkLCB0aGUgZm9sbG93aW5nIHByYWN0aWNhbCBjb25zaWRlcmF0aW9ucyBhcmUgcmVjb21tZW5kZWQgd2hlbiBzZWxlY3RpbmcgdGhlIGZpbmFsIG1vZGVsLg0KDQoqKkltcGxlbWVudGF0aW9uKio6IElmIHlvdSBhcmUgZGVwbG95aW5nIHRoZSBtb2RlbCBpbiBhIHByb2R1Y3Rpb24gZW52aXJvbm1lbnQsIGNvbnNpZGVyIHRoZSBlYXNlIG9mIGltcGxlbWVudGF0aW9uIGFuZCBtYWludGVuYW5jZS4gTGFzc28gbW9kZWxzIGFyZSBvZnRlbiBlYXNpZXIgdG8gaW50ZXJwcmV0IGFuZCBleHBsYWluIHRvIHN0YWtlaG9sZGVycy4NCg0KKipTY2FsYWJpbGl0eSoqOiBGb3IgdmVyeSBsYXJnZSBkYXRhc2V0cywgTGFzc28gbWlnaHQgYmUgbW9yZSBzY2FsYWJsZSBkdWUgdG8gaXRzIHNwYXJzaXR5Lg0KDQoqKlJlZ3VsYXJpemF0aW9uIFBhdGgqKjogSWYgeW91IHdhbnQgdG8gZXhwbG9yZSB0aGUgZW50aXJlIHJlZ3VsYXJpemF0aW9uIHBhdGggKGUuZy4sIGZvciB2aXN1YWxpemF0aW9uIG9yIGFuYWx5c2lzKSwgYWxsIHRocmVlIG1ldGhvZHMgKExhc3NvLCBSaWRnZSwgRWxhc3RpYyBOZXQpIHN1cHBvcnQgdGhpcywgYnV0IExhc3NvIGFuZCBFbGFzdGljIE5ldCBwcm92aWRlIG1vcmUgaW50ZXJwcmV0YWJsZSBwYXRocyBkdWUgdG8gZmVhdHVyZSBzZWxlY3Rpb24uDQoNClRoZSBmb2xsb3dpbmcgdGFibGUgc3VtbWFyaXplZCBzb21lIG9mIHRoZSBjcml0ZXJpYSB0byBiZSBjb25zaWRlcmVkIGluIHNlbGVjdGluZyB0aGUgZmluYWwgd29ya2luZyBtb2RlbCBmcm9tIHRoZSBlcXVhbGx5IHdlbGwtcGVyZm9ybWVkIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbW9kZWxzLg0KDQoNCg0KfCAgIENyaXRlcmlvbiAgIHwJTGFzc28JICAgICB8ICAgUmlkZ2UJICB8ICAgIEVsYXN0aWMgTmV0ICB8DQp8Oi0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS18DQp8CUZlYXR1cmUgU2VsZWN0aW9uCXwJWWVzIChzcGFyc2UgbW9kZWxzKQl8CU5vIChyZXRhaW5zIGFsbCBwcmVkaWN0b3JzKQl8CVllcyAoc3BhcnNlLCBidXQgbGVzcyB0aGFuIExhc3NvKXwJDQp8CU11bHRpY29sbGluZWFyaXR5CXwJSGFuZGxlcyBtb2RlcmF0ZWx5CXwJSGFuZGxlcyB3ZWxsCXwJSGFuZGxlcyB3ZWxsfAkNCnwJSW50ZXJwcmV0YWJpbGl0eQl8CUhpZ2ggKGZld2VyIHByZWRpY3RvcnMpCXwJTG93ZXIgKGFsbCBwcmVkaWN0b3JzIHJldGFpbmVkKQl8CU1vZGVyYXRlfAkNCnwJQ29tcHV0YXRpb25hbCB8CUNvc3QJfAlMb3cJTG93CXwJTW9kZXJhdGV8CQ0KfAlVc2UgQ2FzZQl8CUhpZ2gtZGltZW5zaW9uYWwgZGF0YSwgZmVhdHVyZSBzZWxlY3Rpb24JfAlNdWx0aWNvbGxpbmVhcml0eSwgc21hbGwgZGF0YXNldHMJfAlCYWxhbmNlZCBhcHByb2FjaCwgZ3JvdXBlZCBwcmVkaWN0b3JzfAkNCg0KDQojIEFib3V0IEluZmVyZW5jZSBvZiBSZWd1bGFyaXplZCBSZWdyZXNzaW9uDQoNClJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gaXMgYSB0ZWNobmlxdWUgdXNlZCB0byBwcmV2ZW50IG92ZXJmaXR0aW5nIGluIHN0YXRpc3RpY2FsIG1vZGVscywgcGFydGljdWxhcmx5IGluIHRoZSBjb250ZXh0IG9mIGxpbmVhciByZWdyZXNzaW9uLiBPdmVyZml0dGluZyBvY2N1cnMgd2hlbiBhIG1vZGVsIGxlYXJucyB0aGUgbm9pc2UgaW4gdGhlIHRyYWluaW5nIGRhdGEgcmF0aGVyIHRoYW4gdGhlIHVuZGVybHlpbmcgcGF0dGVybiwgbGVhZGluZyB0byBwb29yIGdlbmVyYWxpemF0aW9uIG9uIHVuc2VlbiBkYXRhLiBSZWd1bGFyaXphdGlvbiBpbnRyb2R1Y2VzIGEgcGVuYWx0eSB0ZXJtIHRvIHRoZSBsb3NzIGZ1bmN0aW9uLCB3aGljaCBjb25zdHJhaW5zIHRoZSBtYWduaXR1ZGUgb2YgdGhlIG1vZGVsJ3MgY29lZmZpY2llbnRzLCB0aGVyZWJ5IGNvbnRyb2xsaW5nIHRoZSBtb2RlbCdzIGNvbXBsZXhpdHkuDQoNCg0KKipBYm91dCB0aGUgSW5mZXJlbmNlIG9mIFJlZ3VsYXJpemVkIFJlZ3Jlc3Npb24qKg0KDQpJbmZlcmVuY2UgaW4gcmVndWxhcml6ZWQgcmVncmVzc2lvbiBpbnZvbHZlcyB1bmRlcnN0YW5kaW5nIHRoZSBwcm9wZXJ0aWVzIG9mIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnRzIGFuZCBtYWtpbmcgc3RhdGlzdGljYWwgaW5mZXJlbmNlcyBhYm91dCB0aGVtLiBIb3dldmVyLCB0cmFkaXRpb25hbCBtZXRob2RzIG9mIGluZmVyZW5jZSAoZS5nLiwgcC12YWx1ZXMsIGNvbmZpZGVuY2UgaW50ZXJ2YWxzKSBhcmUgbm90IGRpcmVjdGx5IGFwcGxpY2FibGUgZHVlIHRvIHRoZSBiaWFzIGludHJvZHVjZWQgYnkgdGhlIHJlZ3VsYXJpemF0aW9uIHBlbmFsdHkuDQoNCg0KKiAqKkJpYXMtVmFyaWFuY2UgVHJhZGVvZmYqKjogIFJlZ3VsYXJpemF0aW9uIGludHJvZHVjZXMgYmlhcyBpbnRvIHRoZSBtb2RlbCB0byByZWR1Y2UgdmFyaWFuY2UsIGxlYWRpbmcgdG8gYmV0dGVyIGdlbmVyYWxpemF0aW9uLiBUaGlzIHRyYWRlb2ZmIGlzIGNydWNpYWwgZm9yIG1vZGVsIHBlcmZvcm1hbmNlLiANCg0KKiAqKkNob2ljZSBvZiBSZWd1bGFyaXphdGlvbiBQYXJhbWV0ZXIgKCRcbGFtYmRhJCkqKjogVGhlIHJlZ3VsYXJpemF0aW9uIHBhcmFtZXRlciAkXGxhbWJkYSQgY29udHJvbHMgdGhlIHN0cmVuZ3RoIG9mIHRoZSBwZW5hbHR5LiBJdCBpcyB0eXBpY2FsbHkgY2hvc2VuIHZpYSBjcm9zcy12YWxpZGF0aW9uIHRvIGJhbGFuY2UgYmlhcyBhbmQgdmFyaWFuY2UuDQoNCiogKipEZWdyZWVzIG9mIEZyZWVkb20qKjogVGhlIGVmZmVjdGl2ZSBkZWdyZWVzIG9mIGZyZWVkb20gaW4gcmVndWxhcml6ZWQgbW9kZWxzIGFyZSByZWR1Y2VkIGR1ZSB0byB0aGUgY29uc3RyYWludHMgaW1wb3NlZCBieSB0aGUgcGVuYWx0eSB0ZXJtLiBUaGlzIGFmZmVjdHMgdGhlIG1vZGVsJ3MgY29tcGxleGl0eSBhbmQgdGhlIGludGVycHJldGF0aW9uIG9mIHJlc3VsdHMuDQoNCiogKipQb3N0LVNlbGVjdGlvbiBJbmZlcmVuY2UqKjogQWZ0ZXIgc2VsZWN0aW5nIHZhcmlhYmxlcyB1c2luZyBMYXNzbywgdHJhZGl0aW9uYWwgaW5mZXJlbmNlIG1ldGhvZHMgY2FuIGJlIGJpYXNlZC4gVGVjaG5pcXVlcyBsaWtlIHNlbGVjdGl2ZSBpbmZlcmVuY2Ugb3IgYm9vdHN0cmFwIG1ldGhvZHMgYXJlIHVzZWQgdG8gbWFrZSB2YWxpZCBpbmZlcmVuY2VzLg0KDQoqICoqQmF5ZXNpYW4gSW50ZXJwcmV0YXRpb24qKjogUmVndWxhcml6ZWQgcmVncmVzc2lvbiBjYW4gYmUgdmlld2VkIHRocm91Z2ggYSBCYXllc2lhbiBsZW5zLCB3aGVyZSB0aGUgcGVuYWx0eSB0ZXJtIGNvcnJlc3BvbmRzIHRvIGEgcHJpb3IgZGlzdHJpYnV0aW9uIG9uIHRoZSBjb2VmZmljaWVudHMuIEZvciBleGFtcGxlLCBSaWRnZSByZWdyZXNzaW9uIGNvcnJlc3BvbmRzIHRvIGEgR2F1c3NpYW4gcHJpb3IsIGFuZCBMYXNzbyBjb3JyZXNwb25kcyB0byBhIExhcGxhY2UgcHJpb3IuDQoNCg0KKipTb21lIFByYWN0aWNhbCBDb25zaWRlcmF0aW9ucyoqDQoNCiogKipTdGFuZGFyZGl6YXRpb24qKjogSXQgaXMgZXNzZW50aWFsIHRvIHN0YW5kYXJkaXplIHRoZSBwcmVkaWN0b3JzIGJlZm9yZSBhcHBseWluZyByZWd1bGFyaXphdGlvbiwgYXMgdGhlIHBlbmFsdHkgdGVybSBpcyBzZW5zaXRpdmUgdG8gdGhlIHNjYWxlIG9mIHRoZSBjb2VmZmljaWVudHMuDQoNCiogKipDb21wdXRhdGlvbmFsIEVmZmljaWVuY3kqKjogRWZmaWNpZW50IGFsZ29yaXRobXMgbGlrZSBjb29yZGluYXRlIGRlc2NlbnQgYXJlIG9mdGVuIHVzZWQgdG8gc29sdmUgdGhlIG9wdGltaXphdGlvbiBwcm9ibGVtcyBpbiByZWd1bGFyaXplZCByZWdyZXNzaW9uLg0KDQoqICoqSW50ZXJwcmV0YWJpbGl0eSoqOiBSZWd1bGFyaXplZCBtb2RlbHMsIGVzcGVjaWFsbHkgTGFzc28sIGNhbiBlbmhhbmNlIGludGVycHJldGFiaWxpdHkgYnkgc2VsZWN0aW5nIGEgc3Vic2V0IG9mIHJlbGV2YW50IGZlYXR1cmVzLg0KDQoNCg0KSW4gc3VtbWFyeSwgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBpcyBhIHBvd2VyZnVsIHRvb2wgZm9yIGltcHJvdmluZyBtb2RlbCBwZXJmb3JtYW5jZSBhbmQgaW50ZXJwcmV0YWJpbGl0eSwgYnV0IGl0IHJlcXVpcmVzIGNhcmVmdWwgY29uc2lkZXJhdGlvbiBvZiB0aGUgdHJhZGVvZmZzIGFuZCBhcHByb3ByaWF0ZSBtZXRob2RzIGZvciBpbmZlcmVuY2UuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K